SDL播放PCM
阿新 • • 發佈:2021-07-08
// // Created by simp on 2021/7/7. // #include <stdio.h> #include <SDL.h> //每次讀取2幀資料,以1024個取樣點一幀 2通道16bit取樣點為例 #define PCM_BUFFER_SIZE (1024*2*2*2) //音訊PCM資料快取 static Uint8 *s_audio_buf = NULL; //目前讀取的位置 static Uint8 *s_audio_pos = NULL; //快取結束位置 static Uint8 *s_audio_end = NULL; //音訊裝置回撥函式 void fill_audio_pcm(void *userdata, Uint8 *stream, int len) { //SDL_AudioCallback //userdata:SDL_AudioSpec 結構中的使用者自定義資料,一般情況下可以不用。 //stream:該指標指向需要填充的音訊緩衝區 //len:音訊緩衝區的大小(以位元組為單位)1024*2*2 SDL_memset(stream, 0, len); if (s_audio_pos >= s_audio_end)//資料讀取完畢 { printf("read complete\n"); return; } //資料夠了就讀取預設的長度,資料不夠就只讀部分(不夠的時候剩多少就讀多少) int remain_buffer_len = s_audio_end - s_audio_pos; len = (len < remain_buffer_len) ? len : remain_buffer_len; //拷貝資料到stream並調整音量 SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME / 8); printf("len=%d\n", len); s_audio_pos += len;//移動快取指標 } //提取PCM檔案 //ffmpeg -i input.mp4 -t20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm //測試pcm //ffplay -ar 44100 -ac2 -f s16le 44100_16bit_2ch.pcm #undef main int main(int argc, char *argv[]) { int ret = -1; FILE *audio_fd = NULL; SDL_AudioSpec spec; // C:\Users\simp\CLionProjects\SDLaction\cmake-build-debug\44100_16bit_2ch.pcm const char *path = "44100_16bit_2ch.pcm"; size_t read_buffer_len = 0; //SDL_initialize if (SDL_Init(SDL_INIT_AUDIO)) {//確認裝置是否支援AUDIO fprintf(stderr, "Could not initalize SDL -%s\n", SDL_GetError()); return ret; } //開啟PCM檔案 audio_fd = fopen(path, "rb"); if (!audio_fd) { fprintf(stderr, "Failed to open pcm file!\n"); goto _FAIL; } s_audio_buf = (uint8_t *) malloc(PCM_BUFFER_SIZE); //音訊引數設定SDL_AudioSpec spec.freq = 44100;//取樣頻率 spec.format = AUDIO_S16SYS;//取樣點格式 音訊資料格式 spec.channels = 2;//聲道數:1單聲道 2立體聲 spec.silence = 0;//設定靜音的值,因為聲音取樣是有符號的,所以0當然就是這個值 spec.samples = 1024;//23.2ms -> 46.4ms每次讀取的取樣數量,多久產生一次回撥和samples 音訊緩衝區中的取樣個數,要求必須是2的n次方 spec.callback = fill_audio_pcm;//回撥函式 spec.userdata = NULL; // 開啟音訊裝置 if (SDL_OpenAudio(&spec, NULL)) { fprintf(stderr, "Failed to open audio device,%s\n", SDL_GetError()); goto _FAIL; } //play audio SDL_PauseAudio(0);//當pause_on為0的時候即可開始播放音訊資料。設定為1的時候,將會播放靜音的值。 int data_count = 0; while (1) { //從檔案讀取PCM資料 read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd); if (read_buffer_len == 0) { break; } data_count += read_buffer_len;//統計讀取資料總位元組數 printf("now playing %10d bytes data.\n", data_count); s_audio_end = s_audio_buf + read_buffer_len;//更新buffer的結束位置 s_audio_pos = s_audio_buf;//更新buffer的起始位置 // the main thread wait for a moment while (s_audio_pos<s_audio_end) { SDL_Delay(10);//等待PCM資料消耗 } } printf("play PCM finish\n"); //關閉音訊裝置 SDL_CloseAudio(); _FAIL: //release some resources if (s_audio_buf)free(s_audio_buf); if (audio_fd)fclose(audio_fd); SDL_Quit(); return 0; }