SDL2播放PCM数据的两种方案

方案一

使用回调的方式,也就是pull模式

SDL_AudioSpec结构

typedef struct SDL_AudioSpec
{
    // 采样率
    int freq;                   /**< DSP frequency -- samples per second */
    // 音频数据格式 
    SDL_AudioFormat format;     /**< Audio data format */
    // 声道数
    Uint8 channels;             /**< Number of channels: 1 mono, 2 stereo */
    // 音频缓冲区静音值
    Uint8 silence;              /**< Audio buffer silence value (calculated) */
    // 采样帧大小
    Uint16 samples;             /**< Audio buffer size in sample FRAMES (total samples divided by channel count) */
    // 兼容性参数
    Uint16 padding;             /**< Necessary for some compile environments */
    // 音频缓冲区大小
    Uint32 size;                /**< Audio buffer size in bytes (calculated) */
    // 填充音频缓冲区回调函数
    SDL_AudioCallback callback; /**< Callback that feeds the audio device (NULL to use SDL_QueueAudio()). */
    // 用户自定义数据,
    void *userdata;             /**< Userdata passed to callback (ignored for NULL callbacks). */
} SDL_AudioSpec;

完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sdl.h"

static  Uint8* audio_chunk;
static  Uint32  audio_len;
static  Uint8* audio_pos;

void  fill_audio(void* udata, Uint8* stream, int len) {
	//SDL 2.0
	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_len -= len;
}


int main2(int argc, char* argv[])
{

	if (SDL_Init(SDL_INIT_AUDIO || SDL_INIT_TIMER))
	{
		printf("SDL init error\n");
		return -1;
	}


	//SDL_AudioSpec
	SDL_AudioSpec wanted_spec;
	wanted_spec.freq = 44100;
	wanted_spec.format = AUDIO_S16LSB;
	wanted_spec.channels = 2;
	wanted_spec.silence = 0;
	wanted_spec.samples = 512;
	wanted_spec.callback = fill_audio;


	if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {
		printf("can't open audio.\n");
		return -1;
	}

	FILE* fp = nullptr;
		
	fopen_s(&fp, "D:/工程/音视频分析/source/s16.pcm", "rb+");

	if (fp == NULL) {
		printf("cannot open this file\n");
		return -1;
	}
	int pcm_buffer_size = 2048;
	char* pcm_buffer = (char*)malloc(pcm_buffer_size);
	int data_count = 0;
	int readcount = 0;

	//Play
	SDL_PauseAudio(0);

	while (!feof(fp)) 
	{
		readcount = fread(pcm_buffer, 1, pcm_buffer_size, fp);
		

		printf("   Now Playing %10d KBytes data.  %d \n", data_count / 1024, readcount);
		data_count += readcount;
		//Set audio buffer (PCM data)
		audio_chunk = (Uint8*)pcm_buffer;
		//Audio buffer length
		audio_len = readcount;
		audio_pos = audio_chunk;

		while (audio_len > 0)//Wait until finish
			SDL_Delay(1);
		
	}

	free(pcm_buffer);

	SDL_Quit();

	return 0;
}

注意事项:

每次读取的数据 是 (  通道数 x 样本数 x 样本个数字节数) 整数倍

例如:通道数为2 ,每次样本数 1024, 样本深度 f32le(float 32 小端)

那么 pcm_buffer_size 至少是 2 x 1024 x 4 = 8192 个字节,或其他整数倍

方案二

主动添加音频数据到播放数据队列中,push模式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sdl.h"

int main(int argc, char* argv[])
{

	if (SDL_Init(SDL_INIT_AUDIO))
	{
		printf("SDL init error\n");
		return -1;
	}


	//SDL_AudioSpec
	SDL_AudioSpec wanted_spec;
	wanted_spec.freq = 44100;
	wanted_spec.format = AUDIO_S16LSB;
	wanted_spec.channels = 2;
	wanted_spec.silence = 0;
	wanted_spec.samples = 512;
	wanted_spec.callback = nullptr;

	SDL_AudioDeviceID deviceID = 0;
	// 打开设备
	if ((deviceID = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE)) < 2) 
	{

		printf("could not open audio device: %s\n", SDL_GetError());
		
		// 清除所有的子系统
		SDL_Quit();
		return 0;
	}

	SDL_PauseAudioDevice(deviceID, 0);

	FILE* fp = nullptr;

	fopen_s(&fp, "D:/工程/音视频分析/source/s16.pcm", "rb+");
	if (fp == NULL) {
		printf("cannot open this file\n");
		return -1;
	}

	if (fp == NULL) {
		printf("error \n");
	}
	Uint32 buffer_size = 4096;
	char* buffer = (char*)malloc(buffer_size);

	while (true)
	{
		if (fread(buffer, 1, buffer_size, fp) != buffer_size) 
		{
			 printf("end of file\n");
			break;
		}
		SDL_QueueAudio(deviceID, buffer, buffer_size);
	}

	Uint32 residueAudioLen = 0;

	/*while (true)
	{
		residueAudioLen = SDL_GetQueuedAudioSize(deviceID);
		printf("%10d\n", residueAudioLen);

		if (residueAudioLen <= 0)
			break;
		SDL_Delay(1);
	}*/


	
	while (true)
	{

	
	printf("1 暂停 2 继续  3 退出 \n");
	int flag = 0;
		
		
	scanf_s("%d", &flag);

	if (flag == 1)
		SDL_PauseAudioDevice(deviceID, 1);
	else if (flag == 2)
		SDL_PauseAudioDevice(deviceID, 0);
	else if(flag == 3)
		break;
	}

	SDL_CloseAudio();
	SDL_Quit();
	fclose(fp);

	return 0;

猜你喜欢

转载自blog.csdn.net/yunxiaobaobei/article/details/130531382