SDL2播放pcm音频

作为播放器,只会播放视频还是不行的,因此需要研究一下SDL如何播放音频。

SDL播放音频的过程相对于播放视频更为简单,主要流程如下:

初始化工作:

1.初始化SDL

2.根据设定参数打开音频设备

循环播放:

1.播放音频数据

2.延时等待播放完成

#include <stdio.h>
#include <tchar.h>
#include <iostream>
extern "C"
{
#include "SDL.h"
};
using namespace std;

static  Uint8  *audio_chunk;  //音频块
static  Uint32  audio_len;    //音频剩下的长度
static  Uint8  *audio_pos;    //音频当前的位置

//回调函数的作用是填充音频缓冲区,当音频设备需要更多数据的时候会调用该回调函数
//userdata一般不使用,stream是音频缓冲区,len是音频缓冲区大小
void  fill_audio(void *udata, Uint8 *stream, int len) {
	//将stream置0,SDL2必须有的操作
	SDL_memset(stream, 0, len);
	if (audio_len == 0)		//如果没有剩余数据
		return;
	len = (len > audio_len ? audio_len : len);	//尽可能得到更多的数据
	//混音处理
	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
	audio_pos += len;   //更新音频当前的位置
	audio_len -= len;   //更新音频剩下的长度
}

int main(int argc, char* argv[])
{
	//初始化SDL
	if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		cout << "can not initialize the SDL" << endl;
		return -1;
	}

	//设置音频相关参数
	SDL_AudioSpec wanted_spec;
	wanted_spec.freq = 44100;                //PCM采样率,通常为44100
	wanted_spec.format = AUDIO_S16SYS;       //音频格式,16bit
	wanted_spec.channels = 2;                //声道数量,单声道1,立体声2
	wanted_spec.silence = 0;                 //设置静音的值
	wanted_spec.samples = 1024;              //音频缓冲区的采样个数,为2的n次方
	wanted_spec.callback = fill_audio;       //回调函数

	//打开SDL Audio组件
	if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {
		cout << "can not open the audio" << endl;
		return -1;
	}

	//打开视频文件
	const char *audio_path = "audio.pcm";
	FILE *fp = nullptr;
	errno_t err;
	err = fopen_s(&fp, audio_path, "rb+");
	if (fp == NULL) {
		cout << "can not open this file" << endl;
		return -1;
	}

	//For YUV420P
	int pcm_buffer_size = 4096;
	char *pcm_buffer = (char *)malloc(pcm_buffer_size);
	int data_count = 0;  //记录已经播放的数据数量

	while (1) {
		//读取数据
		//pcm_buffer:从音频文件中读出的数据存放到此地址中
		//每次读取一个字节,一共读取pcm_buffer_size个
		if (fread(pcm_buffer, 1, pcm_buffer_size, fp) != pcm_buffer_size) {
			// 如果剩下的数据不满足缓冲块的大小
			fseek(fp, 0, SEEK_SET);//重定位fp到开头
			fread(pcm_buffer, 1, pcm_buffer_size, fp);
			data_count = 0;
		}
		cout << "now playing " << data_count << " bytes data" << endl;
		//更新已经播放的数据数量
		data_count += pcm_buffer_size;
		//设置缓冲区
		audio_chunk = (Uint8 *)pcm_buffer;
		//音频缓冲区长度
		audio_len = pcm_buffer_size;
		audio_pos = audio_chunk;
		//播放PCM数据
		SDL_PauseAudio(0);
		while (audio_len > 0)//判断是否读取完毕
			SDL_Delay(1);
	}
	free(pcm_buffer);
	SDL_Quit();
	return 0;
}

从程序中可以看出,SDL通过注册了一个自定义的回调函数,来保证音频数据能够及时读取到音频播放设备中。

工作的整体思路为:创建一个缓冲区,将音频文件的数据读取到缓冲区中,再通过SDL进行播放。同时需要不断更新关于缓冲区以及音频文件定位的信息。

PCM 格式数据是一种原始的音频数据格式,它并没有进行压缩和编码,因此需要通过一些特定的方式进行播放。 一种简单的方法是使用开源的音频库,如 SDL 或 PortAudio。这些库提供了在多个平台上进行音频播放的 API 接口。使用这些库,你可以将 PCM 数据写入音频设备的缓冲区中,从而实现音频输出。 以下是使用 SDL 库进行 PCM 数据播放的示例代码: ``` #include <SDL2/SDL.h> #define SAMPLE_RATE 44100 #define CHANNELS 2 #define BUFFER_SIZE 4096 int main(int argc, char* argv[]) { // Initialize SDL audio subsystem if (SDL_Init(SDL_INIT_AUDIO) != 0) { SDL_Log("Failed to initialize SDL: %s", SDL_GetError()); return -1; } // Set audio format SDL_AudioSpec desiredSpec; SDL_zero(desiredSpec); desiredSpec.freq = SAMPLE_RATE; desiredSpec.format = AUDIO_S16LSB; desiredSpec.channels = CHANNELS; desiredSpec.samples = BUFFER_SIZE; // Open audio device SDL_AudioDeviceID audioDevice = SDL_OpenAudioDevice(NULL, 0, &desiredSpec, NULL, 0); if (audioDevice == 0) { SDL_Log("Failed to open audio device: %s", SDL_GetError()); return -1; } // Generate PCM data short pcmData[BUFFER_SIZE]; for (int i = 0; i < BUFFER_SIZE; i++) { pcmData[i] = i % 32768 - 16384; } // Play PCM data SDL_QueueAudio(audioDevice, pcmData, sizeof(pcmData)); SDL_PauseAudioDevice(audioDevice, 0); // Wait for audio to finish playing while (SDL_GetAudioDeviceStatus(audioDevice) == SDL_AUDIO_PLAYING) { SDL_Delay(10); } // Close audio device and quit SDL SDL_CloseAudioDevice(audioDevice); SDL_Quit(); return 0; } ``` 在这个示例中,我们使用 SDL_Init() 函数初始化 SDL 库,并使用 SDL_OpenAudioDevice() 函数打开音频设备。然后,我们生成 PCM 数据并使用 SDL_QueueAudio() 函数将其写入音频设备的缓冲区中。最后,我们使用 SDL_PauseAudioDevice() 函数开始播放音频,并使用 SDL_GetAudioDeviceStatus() 函数检查音频是否已经播放完毕。一旦音频播放完毕,我们使用 SDL_CloseAudioDevice() 函数关闭音频设备,并使用 SDL_Quit() 函数退出 SDL 库。 你也可以使用其他音频库,如 PortAudio,方法类似。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值