0
$\begingroup$

Original Post: Electronics Stack Exchange ("Why is my frequency being effectively doubled?")

I'll try to explain everything with detail as this is one will be a hard one to explain.

Essentially what I am trying to achieve is sum both the left & right channel to produce a mono signal to be outputted via I2S as the subwoofer line.

The problem I am having right now is that after summing the samples and sending it back to the peripheral I am observing at least double the frequency compare to the input and I have no idea why. Also at lower frequencies the signal looks chopped (see pictures)

What I have done for you is:

  • Ensured the I2S Clock is running correctly
  • Without summing just sending 1:1 samples and it's a mirror image of the input

The I2S3 DMA is setup as follow:

  • Length = 2048 (Tx_BUFF)

The I2S1 DMA is setup as follow:

  • Length = 4096 (Rx_BUFF)
  • Length - 4096 (Tx_BUFF)

Code: I2S_HALFCOMPLETE_CALLBACK()

void I2S_HALFCOMPLETE_CALLBACK() {

    int * I2S3_TxBUFF = getI2S3_TxBUFF();
    int INSAMPLE_I2S_MONO[1024];
  
  for (int i = 0; i < 2048; i++) {

    if ((i % 2) == 0){ // L Samples

        INSAMPLE_I2S_MONO[i >> 1] = I2S1_RxBUFF[i];

    } else if ((i % 2) == 1){ // R Samples

      if (inputSourceMode == INPUT_INLINE) {

          INSAMPLE_I2S_MONO[(i - 1) >> 1] += I2S1_RxBUFF[i];
          INSAMPLE_I2S_MONO[(i - 1) >> 1]  =  INSAMPLE_I2S_MONO[(i - 1) >> 1] >> 1;
      }
    }
  }

  for (int i = 0; i < 2048; i++) {

    if ((i % 2) == 0) { // L Samples

    

    }   else if ((i % 2) == 1) { // R Samples

    

    }
      
     if (i < 1024) {
         I2S3_TxBUFF[i] =  INSAMPLE_I2S_MONO[i];
      }

  }


}

CODE: I2S_TRANSFERCOMPLETE_CALLBACK()

void I2S_TRANSFERCOMPLETE_CALLBACK() {

    int * I2S3_TxBUFF = getI2S3_TxBUFF();
    int INSAMPLE_I2S_MONO[1024];

  int * I2S3_TxBUFF = getI2S3_TxBUFF();

  int INSAMPLE_I2S_MONO[1024];
  
  for (int i = 2048; i < 4096; i++) {

    if ((i % 2) == 0) { // L Samples

        INSAMPLE_I2S_MONO[(i >> 1)-1024] = I2S1_RxBUFF[i];
        
    } else if ((i % 2) == 1){ // R Samples

             INSAMPLE_I2S_MONO[((i - 1) >> 1) - 1024] += I2S1_RxBUFF[i];
             INSAMPLE_I2S_MONO[((i - 1) >> 1) - 1024]  =  INSAMPLE_I2S_MONO[((i - 1) >> 1) - 1024] >> 1;

    }

  }

  for (int i = 2048; i < 4096; i++) {

    if ((i % 2) == 0) { // L Samples


    } else if ((i % 2) == 1){ // R Samples


    }


    if (i < 3072) {

        I2S3_TxBUFF[i - 1024] =  INSAMPLE_I2S_MONO[i-2048];

    }
  }

Results: @ 6Hz enter image description here

@ 56Hz enter image description here

@ 1kHz enter image description here

@ 4khz enter image description here

UPDATE 1:

I still dont have a clue, but since the frequencies are being doubled, does it have to do with feeding the audio samples at half? For example I am receiving a total of 4096 samples but when summing I am only sending back out 2048. Is that why? Its trying to send 2048 within the 4096 sample period?

UPDATE 2:

Another gut feeling is coming from adding the two samples together. I feel like it has something to do with the sampling rate. Tried output L + R samples and it works fine, but when I combine them the sample rate is doubled.

$\endgroup$
14
  • $\begingroup$ welcome here, might be nice if you linked to your question on electronics.stackexchange.com, where we already multiple weaknesses of your code. Also, no, adding samples doesn't change the rate. How should it? Your frequency isn't doubled, you just have an instable system, probably because your callback takes longer than half a sample period. $\endgroup$ Jun 13 at 20:35
  • $\begingroup$ @MarcusMüller Oh perfect I shall. It doesnt I see. Oh amigo, day three on this I gotta start assuming everything. Reasoning being I removed the "summing" operation and just outputting L and R channels and its working so the only difference is the summing operation. $\endgroup$
    – Pllsz
    Jun 13 at 20:36
  • $\begingroup$ @MarcusMüller Ill test the callback theory for you, give me a second, but I doubt it. $\endgroup$
    – Pllsz
    Jun 13 at 20:39
  • $\begingroup$ that's a typical debugging deadend, you start assuming the impossible instead of the likely but hard to debug. Again, your code probably simply takes too long occasionally – that's hard to see $\endgroup$ Jun 13 at 20:39
  • $\begingroup$ @MarcusMüller I see where you're coming from and Ill do it for you so we can cross it out on the white board $\endgroup$
    – Pllsz
    Jun 13 at 20:41
3
$\begingroup$

Does summing two audio samples affect sampling rate?

No.

Sorry this is one tortured piece of code and it's difficult to read.

A few things to check

  1. Are you using "ping pong" buffers for your DMA? You should have two set of buffers that alternate on each frame: one for receiving/sending data and one for working on it.
  2. Make sure you I2S configuration is correct in terms of number of channels, frame size, etc. Ideally input and output DMA use the same config.
  3. Make sure you don't clip. I'd recommend prescaling the data by -6dB before summing it.
  4. Consider simplifying your code.

It should look something like this:

int *rxBuf, *txBuf;
int i, tmp;

// get pointers to the current set of "work" buffers
rxBuf = getCurrentDMAReceiveBuffer();
txBuf = getCurrentDMATransmitBuffer();
for(i = 0; i < FRAME_LENGTH; i += 2)
{
  // get both channel samples divide by two and sum
  tmp = rxBuf[i]>>1 + rxBuf[i+1]>>1;
  // write to both channels of the the transmit buffer
  txBuf[i] = tmp;
  txBuf[i+1] = tmp;
}
$\endgroup$
3
  • $\begingroup$ I do apologize for the messy code. I mean I am doing the fundamentals that youre doing. But ill check it with youre code. I haev also made sure from 2-3 is good. $\endgroup$
    – Pllsz
    Jun 13 at 22:39
  • $\begingroup$ well, you've apologized for your code to both me and Hilmar now. So, clean it up, finally! Remove all the code that doesn't do anything (for example, your empty ifs), remove the whitespace with no purpose, and then indent/reformat properly. I know this sounds like nitpicking, but clean and mostly, as lean as possible code is really the first step to understanding a problem. It takes away both distractions, and reduces the number of bugs you might accidentally miss. $\endgroup$ Jun 14 at 8:28
  • $\begingroup$ @MarcusMüller, boys or girls. It worked. It finally works. $\endgroup$
    – Pllsz
    Jun 14 at 17:42
0
$\begingroup$

The problem was the summing.

Using Hilmar algorithm above got it working.

The difference between Hilmar's algorithm and mine is that his fills up the buffer with both (L+R), so for example if the array is something like this int * dummyArray[8] all 8 elements are occupied with L+R samples.

The difference with my algorithm is that if we have the same int * dummyArray[8] it would populate L+R samples up to half of the array, so all elements within 0...3 would only have L+R.

Reasoning being an array with [1024] samples plus another array with [1024] samples should still have [1024] elements. This could be the double frequency?

$\endgroup$
2
  • $\begingroup$ Then depends on how you configure you I2S output stream. If you configure this as two channels as well and only fill the first half of the buffer, you will indeed see frequency doubling. The stereo channels are interleaved so if you write is a linear area, you are throwing away half of the samples, your output will appear to be up-sampled by two. Your method would work only if you can configure the output I2S as a mono stream. $\endgroup$
    – Hilmar
    Jun 15 at 13:08
  • $\begingroup$ @Hilmar The I2S Peripheral is stereo by default and cant be changed to mono. SAI you can. I see, thats where I went wrong. I was throwing away half the samples but only because if you add to things together it doesnt increase the elements but cuts it in half. Like explained above $\endgroup$
    – Pllsz
    Jun 15 at 16:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.