I am receiving bytes from a BLE device. The device is recording audio for 5 secs and sends it to an Android phone in pcm format.
- 8khz
- 4 channels
- 16-bit sample (2 bytes per sample).
- number of samples = 160 000
- 5 secs recording
I am a complete beginner in audio programming so trying to construct the wav header, I am following this link.
From the byte stream:
- The 3rd - 10th bytes are the data size to be received (ignore the first 3 bytes)
- the next bytes are the data in the form:
Channel-1 1st data point = 0th & 1st position, where 0th is LSB and 1st is MSB
Channel-2 1st data point = 2nd & 3rd position, where 0th is LSB and 1st is MSB
...
My questions are, how can I represent the above in dart and is my header below ok?
The header are these values in hex, little-endian except for the strings
52 49 46 46 “RIFF”
24 E2 04 00 FILE SIZE = 320044 - 8 = 320036
57 41 56 45 “WAVE”
66 6d 74 20 fmt
10 00 00 00 size of fmt subchunk = 16
01 00 pcm = 1
04 00 channels = 4
40 1F 00 00 sample rate = 8000
00 FA 00 00 byteRate = 64 000
08 00 blockAlign = 8
10 00 bits per sample = 16
64 61 74 61 “data”
00 E2 04 00 size of data = 320000
And this is the above header into a Uint8List to create a File from it with the data appended as Int8List
final header = Uint8List.fromList([ 82, 73, 70, 70, 36, 226, 4, 0, 87, 65, 86, 69, 102, 109, 116, 32, 1, 0, 0, 0, 0, 1, 0, 4, 64, 31, 0, 0, 0, 250, 0, 0, 8, 0, 1, 0, 100, 97, 116, 97, 0, 226, 4, 0, ]);
I was expecting to be able to play the wav file with the right header even if I dont modify the data bytes but keep getting this error:
I/ExoPlayerImpl(19598): Init 5dd78f5 [ExoPlayerLib/2.17.1] [raven, Pixel 6 Pro, Google, 33] W/io.platform.dev(19598): Accessing hidden method Landroid/media/AudioTrack;->getLatency()I (unsupported, reflection, allowed) E/LoadTask(19598): Unexpected exception loading stream E/LoadTask(19598): java.lang.IllegalStateException E/LoadTask(19598): at com.google.android.exoplayer2.util.Assertions.checkState(Assertions.java:84) E/LoadTask(19598): at com.google.android.exoplayer2.extractor.wav.WavHeaderReader.readFormat(WavHeaderReader.java:100) E/LoadTask(19598): at com.google.android.exoplayer2.extractor.wav.WavExtractor.readFormat(WavExtractor.java:175) E/LoadTask(19598): at com.google.android.exoplayer2.extractor.wav.WavExtractor.read(WavExtractor.java:134) E/LoadTask(19598): at com.google.android.exoplayer2.source.BundledExtractorsAdapter.read(BundledExtractorsAdapter.java:127) E/LoadTask(19598): at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1042) E/LoadTask(19598): at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:412) E/LoadTask(19598): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) E/LoadTask(19598): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) E/LoadTask(19598): at java.lang.Thread.run(Thread.java:1012) E/ExoPlayerImplInternal(19598): Playback error E/ExoPlayerImplInternal(19598): com.google.android.exoplayer2.ExoPlaybackException: Source error
See the comments on the question. There are several typos in your hand-crafted header. It's generally best to generate the header parts in code.
Here's an abstract class that you can extend with a concrete class that does most of the work of setting the appropriate values of the base class. (For historical reasons, I've always used the number of 'samples' as the driver to calculate the other values (including the length) but feel free to modify to, say, be driven off the number of bytes.) I've included an example concrete class which seems to give correct values but isn't tested.