cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F7XX/F4XX - How to play a Wave sound backwards (Reverse playback) ?

DB..1
Associate II

I have an STM32F7 Discovery card and I would like to know how to read a Wave sound backwards (reverse playback) via SAI and I2S interfaces ?

I can play a Wave sound forward and change its speed with the library and documentation provided by ST.

Thank you for your help.

10 REPLIES 10

You'd need to reorder the data in memory.

At a file level you'd need to perhaps break down the file into blocks, and seek/read those back-to-front.

For MP3, you'd need to determine the packet boundaries and then unpack each packet in the reverse order, and reverse the words within the unpacked data out of the decoder.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
DB..1
Associate II

Thanks for your reply @Tesla DeLorean.

The problem is that I'm not very good at development.

Would you have an example of code to show me?

Bob S
Principal

No time like the present to get better at development. Take a stab at it and test it. Post your code here along with what does or doesn't work or what EXACTLY you don't understand. For starters, find the Wave file format and write code to extract the data from that.

BTW - if you can hear "Paul is dead" you've got better ears than me.

DB..1
Associate II

Thanks for your reply @Bob S.

I'm working on it and trying this.

As soon as I've given it a try, I'll send an example of my code.

Thank you.

DB..1
Associate II

Here is an extract from my code :

#define AUDIO_OUT_BUFFER_SIZE (uint32_t)(WAV_PACKET_SZE*2)    /* (480*2*2*2): Output double buffer of 960 stereo samples in 16-bits representing 10ms frame at 48kHz
 
/* Some structures definitions: from audiofwk_types.h */
typedef struct
  {
    uint32_t buffSze;
    uint32_t readPtr;  /*index in buffer: 0...buffSze*/
    uint32_t writePtr; /*index in buffer: 0...buffSze*/
    uint8_t *pBuff;    /*buffer pointer*/
  } FWK_MEMPOOL_t;
 
  typedef struct
  {
    FWK_MEMPOOL_t* pMemPool; /*used for MemPool Buffer management*/
    void* pData0;
    void* pData1;
  } AUDIO_HANDLE_t;
 /* -------------------------------------------------------------- */
/* Audio buffer control struct */
typedef struct {
  uint8_t buff[AUDIO_OUT_BUFFER_SIZE];
  BUFFER_StateTypeDef state;   /* empty (none), half, full*/
  uint32_t fptr;
}AUDIO_OUT_BufferTypeDef;
 
/*Double BUFFER for Output Audio stream*/
static AUDIO_OUT_BufferTypeDef  BufferCtl;  /* to the codec */
 
/* Process MemPool and Fill complete double output buffer at first time */
MemPoolToSrc(AUDIO_OUT_BUFFER_SIZE/2);
 
/*  Play complete double output buffer at first time */
/*DMA stream from output double buffer to codec in Circular mode launch*/
BSP_AUDIO_OUT_Play((uint16_t*)&BufferCtl.buff[0], AUDIO_OUT_BUFFER_SIZE);
 
 
 
 
 
/**
* @brief  This function Manages the DMA Transfer interrupts callback processing.
* @param  uint16_t offset
* @retval none
*/
void AudioOutCallBackProcess(uint16_t offset)
{
  MemPoolToSrc(offset);
  BufferCtl.state = BUFFER_OFFSET_NONE;
}
 
/**
* @brief  Manages the DMA Complete Transfer interrupt
*         Calculates the remaining file size and new position of the pointer.
* @param  None
* @retval None
*/
void BSP_AUDIO_OUT_TransferComplete_CallBack(void)
{
  if(AudioState == AUDIO_STATE_PLAY)
  {
    BufferCtl.state = BUFFER_OFFSET_FULL;
    AudioOutCallBackProcess(AUDIO_OUT_BUFFER_SIZE/2);
  }
}
 
/**
* @brief  Manages the DMA Half Transfer interrupt.
* @param  None
* @retval None
*/
void BSP_AUDIO_OUT_HalfTransfer_CallBack(void)
{
  if(AudioState == AUDIO_STATE_PLAY)
  {
    BufferCtl.state = BUFFER_OFFSET_HALF;
    AudioOutCallBackProcess(0);
  }
}
 
/**
* @brief  Read from USB a number of samples such that after SRC conversion
*         480 stereo samples are provided to the codec each 10 ms
* @param  bytesread: returns to the caller the bytes read from USB
* @param  offset: first flip flop buffer or second flip flop buffer
* @retval none
*/
 
void MemPoolToSrc(uint16_t offset)
{
  uint32_t bytesread;
 
  /* Read from memory pool1 to temporary module processing computation buffer */
  FWK_MEMPOOL_Read(hptr->pMemPool, (uint8_t*)&BufferCtl.buff[offset],  AUDIO_OUT_BUFFER_SIZE/2);
 
  bytesread=AUDIO_OUT_BUFFER_SIZE/2; /*force value*/
  BufferCtl.fptr += bytesread;
}
 
/**
* @brief  Routine to read (extract) from the memory pool
* @param  FWK_MEMPOOL_t *pMemPool : Memory pool handler
* @param  uint8_t *pData : destination buffer
* @param  uint32_t DataSze : number of bytes to copy
* @retval none
*/
FWK_MEMPOOL_Read (FWK_MEMPOOL_t *pMemPool, uint8_t *pData, uint32_t DataSze)
{
  uint32_t tmp, tmpData = 0;
	
  if(pMemPool->readPtr + DataSze <= pMemPool->buffSze)
  {
    memcpy(pData, pMemPool->pBuff + pMemPool->readPtr, DataSze);
    pMemPool->readPtr += DataSze;
    if(pMemPool->readPtr == pMemPool->buffSze)
    {
      pMemPool->readPtr = 0;
    }
  }
  else
  {
    tmp = pMemPool->buffSze - pMemPool->readPtr;
    memcpy(pData, pMemPool->pBuff + pMemPool->readPtr, tmp);
    pMemPool->readPtr = 0;
 
    memcpy(pData + tmp, pMemPool->pBuff, DataSze - tmp);
    pMemPool->readPtr = DataSze - tmp;
  }
}

DB..1
Associate II

First, I tried to invert the buffer data (in FWK_MEMPOOL_Read function) :

uint8_t *tmp_pData;
uint8_t i = 0;
 
*/ Backup pData */
memcpy(tmp_pData, pData, DataSze);
 
/* data inversion */
for (uint8_t bit = DataSze - 1; i <= 0; i --) /* decrement */
{
    pData[i] = tmp_pData[bit];
    i += 1;
}
*/ Copy the new buffer */
memcpy(pData, tmp_pData, DataSze);

But it doesn't work.

The sound stops after a few milliseconds.

You declare tmp_pData as a pointer, but you never point it TO anything. So your first memcpy() writes to random memory. Maybe RAM, maybe Flash, maybe non-existent memory space. If "DataSize" is a known, fixed size, then use "uint8_t tmp_data[DataSize]" as the declaration (note the change in the variable name since it is no longer a pointer). If DataSize in not known in advance, the issue becomes a bit more involved. The easiest (but maybe not the best) solution is to use malloc/free. But then you are allocating and freeing a buffer for every segment of audio data. May be OK, may not be OK. Could you simply have the DMA start at the end of the buffer and decrement its pointer instead of increment?

Hmmm... question: do you want to play each BUFFER of data backwards? Or do you want to play the entire audio stream backwards? Your code above plays each buffer full backwards. This is like saying "counting from 100 to 1 backwards" is "10 9 8 7 6 5 4 3 2 1" then "20 19 18 17 16 15 14 13 12 11", etc. Obviously not the same as "100 99 98 97 96 ....".

DB..1
Associate II

Thank you @Bob S for your response.

I wanted to use tmp_pData to reverse the words and write it to the pData buffer.

But this was not the right solution.

What I would like is to read each data buffer backwards.

I'm making some clarifications. Namely, I use a circular DMA with a double buffer to read a sound.

The Wave file is stored on a USB stick and is loaded by a routine using the BSP_AUDIO_IN function.

The double buffer is BufferCtl.buff with a size AUDIO_OUT_BUFFER_SIZE

The buffer is only half used with each BSP_AUDIO_OUT_TransferComplete_CallBack and BSP_AUDIO_OUT_HalfTransfer_CallBack call.

BSP_AUDIO_OUT_HalfTransfer_CallBack

                       ||

                         \/

               BufferCtl.buff

[0 ---- > AUDIO_OUT_BUFFER_SIZE/2]

            BSP_AUDIO_OUT_TransferComplete_CallBack

                                            ||

                                             \/

                                   BufferCtl.buff

[AUDIO_OUT_BUFFER_SIZE/2 ---- > AUDIO_OUT_BUFFER_SIZE]

DB..1
Associate II

0693W00000D0pPiQAJ.gif