SDL 与 FFMPEG 音乐播放器开发(2)——混播多个音频

来源:互联网 发布:反美颜软件ios 编辑:程序博客网 时间:2024/06/05 04:31

第一篇总体提了一下SDL,完全没有提到FFMPEG。我的思路是,在说解码之前,你起码要知道怎么使用解码后的文件。

相信大家如果看了网上的一些教程,应该已经能够播放出PCM文件。今天我来谈谈如何播放多个PCM文件。


这回先上代码

#define MAX_MUSIC_DATA 10#define PCM_BUFFER_SIZE 4096
struct AudioData{  Uint8  *audio_chunk;  Uint32  audio_len;  Uint8  *audio_pos;SDL_Thread *thread;AudioState state;FILE *file;char *pcm_buffer;char *filename;float skipSecond;float HzPercent;int originalFreq;int volume;}m_data[MAX_MUSIC_DATA], *pData;/* Audio Callback * The audio function callback takes the following parameters:  * stream: A pointer to the audio buffer to be filled  * len: The length (in bytes) of the audio buffer  * */ void  fill_audio(void *udata,Uint8 *stream,int len){ //SDL 2.0SDL_memset(stream, 0, len);for (int i = 0; i < MAX_MUSIC_DATA; i++){if (m_data[i].audio_len == 0)/*  Only  play  if  we  have  data  left  */continue;len = (len > m_data[i].audio_len ? m_data[i].audio_len : len);/*  Mix  as  much  data  as  possible  */SDL_MixAudio(stream, m_data[i].audio_pos, len, m_data[i].volume);m_data[i].audio_pos += len;m_data[i].audio_len -= len;}} 

在fill_audio()当中将每个数据叠加在audio预设的数据流stream,就是混播音频的关键。

而如何更新每个数据中的pcm_buffer呢 看下一段代码。

int PlayMusicbyName(void* fileName){for (int i = 0; i < MAX_MUSIC_DATA; i++){if (m_data[i].file){if (!strcmp(m_data[i].filename, (char*)fileName)){return -1;}}if (!m_data[i].file){thread[5] = SDL_CreateThread(decodeMusicFile, "decodeMusicFile", fileName);SDL_Delay(500);//SDL_WaitThread(thread[5],0);char buf[256];sprintf(buf, "output/%soutput.pcm", fileName);m_data[i].file = fopen(buf, "rb+");if (!m_data[i].file){printf("This file is not exist!!\n");return -1;}m_data[i].filename = (char *)malloc(sizeof(fileName));strcpy(m_data[i].filename, (char *)fileName);m_data[i].pcm_buffer = (char *)malloc(PCM_BUFFER_SIZE);m_data[i].state = play;m_data[i].skipSecond = -1;m_data[i].HzPercent = -1;m_data[i].volume = 128;pData = &m_data[i];break;}}return 0;}


这段代码本质上就是打开PCM文件,并将文件指针信息存入m_data当中,同时malloc申请一部分内存用来储存读出来的数据。

int PlayMusicWithPCM(void* ptr){if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {printf("Could not initialize SDL - %s\n", SDL_GetError());return -1;}//SDL_AudioSpecSDL_AudioSpec wanted_spec;wanted_spec.freq = 44100;wanted_spec.format = AUDIO_S16SYS;wanted_spec.channels = 2;wanted_spec.silence = 0;wanted_spec.samples = 1024;wanted_spec.callback = fill_audio;m_OriginalFreq = wanted_spec.freq;if (SDL_OpenAudio(&wanted_spec, NULL) < 0){printf("can't open audio.-%s\n", SDL_GetError());return -1;}while (1){for (int i = 0; i < MAX_MUSIC_DATA; i++){bool shouldwait = false;if (m_data[i].state == play){if (fread(m_data[i].pcm_buffer, 1, PCM_BUFFER_SIZE, m_data[i].file) != PCM_BUFFER_SIZE){// Loopfseek(m_data[i].file, 0, SEEK_SET);fread(m_data[i].pcm_buffer, 1, PCM_BUFFER_SIZE, m_data[i].file);}//Set audio buffer (PCM data)m_data[i].audio_chunk = (Uint8 *)m_data[i].pcm_buffer;//Audio buffer lengthm_data[i].audio_len = PCM_BUFFER_SIZE;m_data[i].audio_pos = m_data[i].audio_chunk;}}//PlaySDL_PauseAudio(0);while (1)//Wait until finish{bool WAIT = false;for (int i = 0; i < MAX_MUSIC_DATA; i++){if (m_data[i].audio_len > 0){WAIT = true;break;}}if (WAIT){SDL_Delay(1);}else{break;}}}SDL_CloseAudio();return 0;}

每次循环的时候都更新数据的buffer、pos等信息,在每次SDL_PauseAudio回调的时候都能够播放下一时刻的数据(注意 这里必须把每一个PCM文件的数据都进行更新)。


总结一下,SDL混播PCM数据的思路如下:


读取每一个文件的信息----->将待播放部分存入buffer----->将每一个文件的buffer进行mix(使用mixAudio)------>更新数据

到这里,我们就可以把未经过压缩的PCM数据进行播放了,大家可以试一试,感受一下同时播放多个声音带来的成就感与杂乱感。


以及大家可以联系我125650971@qq.com

1 0
原创粉丝点击