cancel
Showing results for 
Search instead for 
Did you mean: 

How to play audio files using STM32 Part 3

B.Montanari
ST Employee

How to play audio files using STM32?

Welcome back!
In this last part we'll cover the interrupt implementation and the final results, so stay tuned

In the interrupt service routine, more specifically in the TIM3 periodic interrupt, we must execute a few actions as well:
  1. Include the adpcm header
  2. Check if the audio file to be played has changed or not
  3. Clear the interrupt flag
  4. Decode the audio file
  5. Repeat the same position of the audio file 8 times
  6. Output the decoded value in the PWM or DAC
  7. Once the 8 repetitions are made, move to the next decoded position in the vector
  8. If the audio file was fully played, stop the TIM3 interrupt to stop the audio output
Translating the steps above into actual code please locate it in the stm32g0xx_it.c we have it as shown below, please track the code position by verifying the /* USER CODE BEGIN*/ and /* USER CODE END */
 
/* USER CODE BEGIN Includes */
#include "adpcm.h"
#ifdef USE_DAC
#include "dac.h"
#endif
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
tTwoByte newSample;
extern AudioElement AudioFile;
extern uint8_t AudioFileToPlay;
/* USER CODE END PV */

/**
  * @brief This function handles TIM3 global interrupt.
  */
void TIM3_IRQHandler(void)
{
  /* USER CODE BEGIN TIM3_IRQn 0 */
	uint8_t  adpcmSample;
	static uint16_t pcmSample;
	static uint8_t nibble = 1;
	static uint8_t repetition = 0;
	static uint16_t sample_position = 0;
	static unsigned char *RawAudio;
	static uint8_t PrevAudioFileToPlay = 0xFF;

	if(PrevAudioFileToPlay != AudioFileToPlay)
	{
		PrevAudioFileToPlay = AudioFileToPlay;
		nibble = 1;
		repetition = 0;
		sample_position = 0;
		RawAudio = (unsigned char *)AudioFile.AudioFiles[AudioFileToPlay];
	}

	if (LL_TIM_IsActiveFlag_UPDATE(TIM3))
	{
		LL_TIM_ClearFlag_UPDATE(TIM3);

		if ((repetition==0) & (sample_position < AudioFile.AudioSize[AudioFileToPlay]))
		{  // new sample is generated
			repetition = 7;	// reinitialize repetition down counter
			if (nibble)
			{   // first 4 bits of the ADPCM byte decoded
				adpcmSample = (uint8_t)(RawAudio[sample_position] >> 4);
			}
			else
			{   // last 4 bits of the ADPCM byte decoded
				adpcmSample = (uint8_t)(RawAudio[sample_position] & 0x0F);
				sample_position++ ;
			}

			nibble = (uint8_t)(!nibble);/* indicator inverted mean next interrupt will handle
																					 the second part of the byte.  */
			pcmSample = ADPCM_Decode(adpcmSample);

			// update sample
			newSample.uShort = (uint16_t)32768 + pcmSample;
			TIM3->CCR2 = newSample.uBytes[0]; //LSB
			TIM3->CCR1 = newSample.uBytes[1]; //MSB
#ifdef USE_DAC
			HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, (newSample.uShort)>>4);
#endif
		}
		else if (sample_position < AudioFile.AudioSize[AudioFileToPlay])
		{  // repetition 7 more times of the PWM period before new sample, total of times the same value is repeated = 8
			repetition--;

			// reload Timer with the actual sample value
			newSample.uShort = (uint16_t)32768 + pcmSample;
			TIM3->CCR2 = newSample.uBytes[0]; //LSB
			TIM3->CCR1 = newSample.uBytes[1]; //MSB
#ifdef USE_DAC
			HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, (newSample.uShort)>>4);
#endif
		}
		else
		{  // end of the audio clip
			/* Disable the TIM3 Interrupt */
			NVIC_DisableIRQ(TIM3_IRQn);
			// stop the timer
			LL_TIM_DisableCounter(TIM3);
		}

	}
	return;
  /* USER CODE END TIM3_IRQn 0 */
  /* USER CODE BEGIN TIM3_IRQn 1 */

  /* USER CODE END TIM3_IRQn 1 */
}
Now you have all the steps prepared and you can build your application by pressing “Ctrl+B”. You should see 0 errors and 0 warnings and in the memory details you should have the .myAudioFiles with the size of your audio file:
534.png
Assuming you have your hardware ready and properly connected, you can load the program in the STM32 and play with your audio file! You can add as many audio files your memory can withstand by repeating the process shown in this article
Conclusion:
The ADPCM encode and decode functions available in the software pack allows a rather small implementation to play audio files / music with all STM32. With some additional steps, you can enhance the storage to an external memory and create your own audio player
Enjoy your STM32 playing musics!
Part 1
Part 2
Part 3
 
Comments
rperu.1
Associate

Hello, thank you for this nice arcticle. It says that with additional steps the audio can be read from a file stored in external memory. Is there an existing article describing how to play a music file this way ? And if possible, using the SAI peripheral and PCM protocol.

Randsmok
Associate

Hello.

I'm interested too about loading file from external memory.

For now I've run this project on NUCLEO L476RG and everything is working fine except one thing. Only first few second are played.

My *.wav file is almost 40 seconds long and it has 6,72MB. After converting the *.ima file has 305kb. The *.c file has 1882kb and memory size described in header of this file is as below:

/*StartOffset(h): 00000000, EndOffset(h): 0004C489, Długość(h): 0004C48A */

The original file size from this article is changed to 312458b (#define ADPCMD_PI 312458).

First question: What I should change to play whole file?

Second question: Could you write the next part of this article - How to load files from external memory? 

Best regards,

Sebastian.

 

 

lubo
Associate

Hello 

What to add/modify in code to play two wav files ?

Version history
Last update:
‎2022-06-22 06:46 AM
Updated by: