Stereo Queue Interleave

Status
Not open for further replies.

panphonic

New member
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: LeftSampleRightSampleLeftSampleRightSampleLeftSampleRightSample 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:

Code:
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:

Code:
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
 
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".
 
Sorry Paul, here is the full code.

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

Code:
#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); 



}
 
Just to update on my progress with debugging this, it appears to be this routine which is the problem:

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

I have isolated this part of the code in another sketch:
Code:
#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

Code:
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
 
Last edited:
Hmm, just dived into the assembler code in memcpy_tointerleaveLR().

Code:
/* 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:

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

when I changed this to:

Code:
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
 
Status
Not open for further replies.
Back
Top