PDA

View Full Version : Stereo Queue Interleave



panphonic
03-31-2016, 01:46 PM
I have been working on an audio recorder with teensy. Mono works fine, and I have WAV header writing sorted too. Just trying to write a stereo version.

In a stereo audio file the samples are interleaved like this: LeftSampleRightSampleLeftSampleRightSampleLeftSamp leRightSample etc. But the queue object returns 128 sample blocks. I have tried interleaving the samples using two queue objects, reading blocks into two left/right buffer arrays and then using a loop to combine these arrays one sample at a time. This works, but is too slow.

I just saw that there is an object memcpy_tointerleaveLR() in the library. This seems to be used internally to interleave audio streams for the I2s output, but would it be possible to use this to interleave buffers like this:



int16_t bufferLR[256];
memcpy_tointerleaveLR(bufferLR,queue1.readBuffer() ,queue2.readBuffer());

I have tried this, and the code only works if the buffer array is int16_t, but the SD library only writes bytes. Then tried this:


byte SDbuffer[512];
memcpy(SDbuffer,bufferLR,512);

which compiles and runs, but records a digital glitch, not audio.

I'm not sure how to debug this, can anyone help?

James

PaulStoffregen
03-31-2016, 02:16 PM
Sounds like you're on the right track. Whatever's wrong is likely some little thing. But nobody's going to guess and give you a useful answer without seeing your program.... which is why we have the "Forum Rule".

panphonic
03-31-2016, 02:35 PM
Sorry Paul, here is the full code.

I have left the 'loop' version of my code in the stopRecording() routine for reference.


#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <memcpy_audio.h>


// GUItool: begin automatically generated code
AudioInputI2S i2s1; //xy=370,155
AudioRecordQueue queue1; //xy=647,144
AudioRecordQueue queue2; //xy=647,185
AudioConnection patchCord1(i2s1, 0, queue1, 0);
AudioConnection patchCord2(i2s1, 0, queue2, 0);
AudioControlSGTL5000 sgtl5000_1; //xy=563,357
// GUItool: end automatically generated code

const int myInput = AUDIO_INPUT_MIC;
unsigned long ChunkSize = 0L;
unsigned long Subchunk1Size = 16;
unsigned int AudioFormat = 1;
unsigned int numChannels = 2;
unsigned long sampleRate = 44100;
unsigned int bitsPerSample = 16;
unsigned long byteRate = sampleRate*numChannels*(bitsPerSample/8);// samplerate x channels x (bitspersample / 8)
unsigned int blockAlign = numChannels*bitsPerSample/8;
unsigned long Subchunk2Size = 0L;

unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;

// The file where data is recorded
File frec;
elapsedMillis timea;
int rec = 1;

void setup() {
// Audio connections require memory, and the record queue
// uses this memory to buffer incoming audio.
AudioMemory(100);

// Enable the audio shield, select input, and enable output
sgtl5000_1.enable();
sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.5);
sgtl5000_1.micGain(30);

// Initialize the SD card
SPI.setMOSI(7);
SPI.setSCK(14);
if (!(SD.begin(10))) {
// stop here if no SD card, but print a message
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
startRecording();
timea = 0;
}


void loop() {
if (timea > 30000){
rec = 0;
}

if (rec == 1){
continueRecording();
}

if (rec == 0){
stopRecording();
delay(1000);
Serial.println("stopped");
exit(0);
}

}


void startRecording() {
Serial.println("startRecording");
if (SD.exists("RECORD.WAV")) {
// The SD library writes new data to the end of the
// file, so to start a new recording, the old file
// must be deleted before new data is written.
SD.remove("RECORD.WAV");
}
frec = SD.open("RECORD.WAV", FILE_WRITE);
if (frec) {
queue1.begin();
queue2.begin();
}
}

void continueRecording() {
if (queue1.available() >= 1 && queue2.available() >= 1) {

int16_t bufferLR[256];
byte bufferLRbyte[512];

memcpy_tointerleaveLR(bufferLR,queue1.readBuffer() ,queue2.readBuffer());
queue1.freeBuffer();
queue2.freeBuffer();
memcpy(bufferLRbyte,bufferLR,512);

// write all 512 bytes to the SD card

// elapsedMicros usec = 0;
frec.write(bufferLRbyte, 512);
recByteSaved += 512;


}

}
}



void stopRecording() {
queue1.end();
queue2.end();
Serial.println("stoppedRecording");
Serial.print(queue1.available());
Serial.println(" packets left to write");
Serial.print("Max Queue Length");
Serial.println(maxQueueLength);
// buffer arrays for left and right channels,
// and one for LR interleaved
byte bufferL[256];
byte bufferR[256];
byte bufferLR[512];

// Copy one block from each channel into
// a buffer, then free the buffer
while (queue2.available() > 0) {
memcpy(bufferL, queue1.readBuffer(), 256);
memcpy(bufferR, queue2.readBuffer(), 256);
queue1.freeBuffer();
queue2.freeBuffer();


//Interleave two bytes (one 16bit sample) from the
//left and right buffers one after another

int b = 0;
for (int i=0; i <= 511; i=i+4){
bufferLR[i] = bufferL[b];
bufferLR[i+1] = bufferL[b+1];
bufferLR[i+2] = bufferR[b];
bufferLR[i+3] = bufferR[b+1];
b = b+2;
}
// write all 512 bytes to the SD card

elapsedMicros usec = 0;
frec.write(bufferLR, 512);
recByteSaved += 512;
Serial.print("SD write, us=");
Serial.println(usec);


}

writeOutHeader();
}

void writeOutHeader() { // update WAV header with final filesize/datasize

// NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
// Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
Subchunk2Size = recByteSaved;
ChunkSize = Subchunk2Size + 36;
frec.seek(0);

frec.write("RIFF");
byte1 = ChunkSize & 0xff;
byte2 = (ChunkSize >> 8) & 0xff;
byte3 = (ChunkSize >> 16) & 0xff;
byte4 = (ChunkSize >> 24) & 0xff;
frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
frec.write("WAVE");
frec.write("fmt ");
byte1 = Subchunk1Size & 0xff;
byte2 = (Subchunk1Size >> 8) & 0xff;
byte3 = (Subchunk1Size >> 16) & 0xff;
byte4 = (Subchunk1Size >> 24) & 0xff;
frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
byte1 = AudioFormat & 0xff;
byte2 = (AudioFormat >> 8) & 0xff;
frec.write(byte1); frec.write(byte2);
byte1 = numChannels & 0xff;
byte2 = (numChannels >> 8) & 0xff;
frec.write(byte1); frec.write(byte2);
byte1 = sampleRate & 0xff;
byte2 = (sampleRate >> 8) & 0xff;
byte3 = (sampleRate >> 16) & 0xff;
byte4 = (sampleRate >> 24) & 0xff;
frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
byte1 = byteRate & 0xff;
byte2 = (byteRate >> 8) & 0xff;
byte3 = (byteRate >> 16) & 0xff;
byte4 = (byteRate >> 24) & 0xff;
frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
byte1 = blockAlign & 0xff;
byte2 = (blockAlign >> 8) & 0xff;
frec.write(byte1); frec.write(byte2);
byte1 = bitsPerSample & 0xff;
byte2 = (bitsPerSample >> 8) & 0xff;
frec.write(byte1); frec.write(byte2);
frec.write("data");
byte1 = Subchunk2Size & 0xff;
byte2 = (Subchunk2Size >> 8) & 0xff;
byte3 = (Subchunk2Size >> 16) & 0xff;
byte4 = (Subchunk2Size >> 24) & 0xff;
frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4);
frec.close();
Serial.println("header written");
Serial.print("recbytesaved ");
Serial.println(recByteSaved);
Serial.print("Subchunk2");
Serial.println(Subchunk2Size);



}

panphonic
04-02-2016, 09:08 PM
Just to update on my progress with debugging this, it appears to be this routine which is the problem:


memcpy_tointerleaveLR(bufferLR,queue1.readBuffer() ,queue2.readBuffer());

I have isolated this part of the code in another sketch:

#include <SPI.h>
#include <SD.h>
#include <memcpy_audio.h>

void setup() {
// put your setup code here, to run once:

// SPI.setMOSI(7);
// SPI.setSCK(14);
// if (!(SD.begin(10))) {
// // stop here if no SD card, but print a message
// while (1) {
// Serial.println("Unable to access the SD card");
// delay(500);
// }
//}
}

void loop() {
File frec;

delay(2000);
// if (SD.exists("test.raw")) {
// // The SD library writes new data to the end of the
// // file, so to start a new recording, the old file
// // must be deleted before new data is written.
// SD.remove("test.raw");
// }
// frec = SD.open("test.raw", FILE_WRITE);

int16_t bufferL[128];
int16_t bufferR[128];
int16_t bufferLR[256];

for (int i=0; i<=127; i++){
bufferL[i]=i;
bufferR[i]=i+1000;
}



memcpy_tointerleaveLR(bufferLR,bufferL,bufferR);

for (int i=0; i<=255; i++){
Serial.println(bufferLR[i]);
}

exit(0);

}

and the output in the serial monitor is correct up until halfway through the data (the 64th L/R sample) here is the output


0
1000
1
1001
2
1002
3
1003
4
1004
5
1005
6
1006
7
1007
8
1008
9
1009
10
1010
11
1011
12
1012
13
1013
14
1014
15
1015
16
1016
17
1017
18
1018
19
1019
20
1020
21
1021
22
1022
23
1023
24
1024
25
1025
26
1026
27
1027
28
1028
29
1029
30
1030
31
1031
32
1032
33
1033
34
1034
35
1035
36
1036
37
1037
38
1038
39
1039
40
1040
41
1041
42
1042
43
1043
44
1044
45
1045
46
1046
47
1047
48
1048
49
1049
50
1050
51
1051
52
1052
53
1053
54
1054
55
1055
56
1056
57
1057
58
1058
59
1059
60
1060
61
1061
62
1062
63
1063
30977
-19040
15272
-14630
19216
-14312
8188
9885
10784
-11639
-29569
-28550
-3729
-31549
8073
-7650
22097
25268
-7319
15072
10776
9439
22082
-12333
13354
3284
-16097
-19329
4912
-12085
-21588
-349
-18854
18915
-6993
23790
-12008
18216
-27129
11223
19641
8260
-10289
21163
770
16898
1987
0
-27612
8191
300
0
256
16898
246
0
-28532
8191
0
0
-32624
8191
-31600
8191
0
0
0
0
1
0
14649
0
-31578
8191
8320
16391
8320
16391
-32768
8191
-27072
8191
29973
834
66
0
24000
0
0
0
0
0
0
0
0
0
0
0
-7
-1
0
0
-29127
14563
1
0
-27072
8191
0
0
-29127
14563
0
0
0
0
-32764
16395
9
0
20364
0
14
0
0
0
4865
0


I have a feeling I am misinterpreting the function of memcpy_tointerleaveLR() but I can't understand the assembler code. Does anyone have any pointers?

J

panphonic
04-02-2016, 09:34 PM
Hmm, just dived into the assembler code in memcpy_tointerleaveLR().


/* void memcpy_tointerleave(short *dst, short *srcL, short *srcR); */
.global memcpy_tointerleaveLR
.thumb_func
memcpy_tointerleaveLR:

@ r0: dst
@ r1: srcL
@ r2: srcR

push {r4-r11,r14}
add r14,r0,#256 // TODO: 256 = AUDIO_BLOCK_SAMPLES*2
.align 2
.loopLR:

.irp offset, 1,2

//Load 2*4 words
ldmia r1!, {r5,r7,r9,r11} //1+4
ldmia r2!, {r6,r8,r10,r12} //1+4

pkhbt r3,r5,r6,LSL #16 //1
pkhtb r4,r6,r5,ASR #16 //1

pkhbt r5,r7,r8,LSL #16 //1
pkhtb r6,r8,r7,ASR #16 //1

pkhbt r7,r9,r10,LSL #16 //1
pkhtb r8,r10,r9,ASR #16 //1

pkhbt r9,r11,r12,LSL #16 //1
pkhtb r10,r12,r11,ASR #16 //1

//Write 8 Words
stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10} //1+8

.endr //5+5+8+9 = 27 Cycles to interleave 32 bytes.

cmp r14, r0
bne .loopLR

pop {r4-r11,r14}
BX lr

The only line that I could see having an effect on this was:


add r14,r0,#256 // TODO: 256 = AUDIO_BLOCK_SAMPLES*2

when I changed this to:


add r14,r0,#512 // TODO: 256 = AUDIO_BLOCK_SAMPLES*2

My code miraculously started working and it records stereo WAV files with no buffer overruns.

Not sure whether this is a bug in the memcpy_tointerleaveLR() assembler code, or if I am just using it for something it is not designed to do...

Maybe Paul or Frank or someone who actually understands the assembler code could take a look and see whats going on?

J