SDL2.0播放pcm格式音频

音频信号
  音频信号(acoustic signals)是带有语音、音乐和音效的有规律的声波的频率、幅度变化信息载体。根据声波的特征,可把音频信息分类为规则音频和不规则声音。其中规则音频又可以分为语音、音乐和音效。规则音频是一种连续变化的模拟信号,可用一条连续的曲线来表示,称为声波。
  声音的三个要素是音调、音强和音色。声波或正弦波有三个重要参数:频率 ω0、幅度An和相位ψn ,这也就决定了音频信号的特征。
  对音频信号进行采样,模拟信号数字化后,就是数字音频信号了。

数字音频信号
  数字音频计算机数据的存储是以0、1的形式存取的,那么数字音频就是首先将音频文件转化,接着再将这些电平信号转化成二进制数据保存,播放的时候就把这些数据转换为模拟的电平信号再送到喇叭播出,数字声音和一般磁带、广播、电视中的声音就存储播放方式而言有着本质区别。相比而言,它具有存储方便、存储成本低廉、存储和传输的过程中没有声音的失真、编辑和处理非常方便等特点。
  数字音频信号,就是我们最终处理的音频数据。
  音频数字信号信号具备几个特征:

量化级
  简单地说就是描述声音波形的数据是多少位的二进制数据,通常用bit做单位,如16bit、24bit。16bit量化级记录声音的数据是用16位的二进制数,因此,量化级也是数字声音质量的重要指标。我们形容数字声音的质量,通常就描述为24bit(量化级)、48KHz采样,比如标准CD音乐的质量就是16bit、44.1KHz采样。

声道
  可以简单的理解为通过一个振膜采样到的音频数据就是一个声道,两个振膜就是两个声道,以此类推。振膜一般有大、中、小三种尺寸,尺寸越大,对声波越敏感,成本也越高。一个麦克风里面有的有一个振膜,有的有两个振膜。一个振膜的麦克风进行的是Mono单声道录音,两个振膜的麦克风进行的是Stereo双声道立体声录音。五声道环绕立体声录音就是麦克风1录取东北方向的声音,麦克风2录取西北方向的声音,麦克风3录取西南方向的声音,麦克风4录取东南方向的声音,麦克风5录取正前方的声音。另外还有四声道环绕立体声录音和七声道环绕立体声录音。

采样率
  简单地说就是通过波形采样的方法记录1秒钟长度的声音,需要多少个数据。44KHz采样率的声音就是要花费44000个数据来描述1秒钟的声音波形。原则上采样率越高,声音的质量越好。

比特率
  按键渲染颜色一种数字音乐压缩效率的参考性指标,表示记录音频数据每秒钟所需要的平均比特值(比特是电脑中最小的数据单位,指一个0或者1的数),通常我们使用Kbps(通俗地讲就是每秒钟1024比特)作为单位。CD中的数字音乐比特率为1411.2Kbps(也就是记录1秒钟的CD音乐,需要1411.2×1024比特的数据),近乎于CD音质的MP3数字音乐需要的比特率大约是112Kbps~128Kbps。

压缩率
  通常指音乐文件压缩前和压缩后大小的比值,用来简单描述数字声音的压缩效率。

步骤一:初始化子系统
  初始化音频系统,其他多余的系统不用初始化。

步骤二:根据音频信息打开音频设备
  填充好SDL_AudioSpec音频信息,打开音频设备,此时会返回最接近的音频设备,若没有接近的则第二个参数返回0,此时我们直接第二个参数如0,无需返回。

步骤三:开始播放
  使用SDL_PauseAudio(0)进行播放。

步骤四:循环补充数据
  根据缓冲区数据长度和文件剩余的数据长度进行补充,若缓冲区数据没了,就补充一次,使用SDL_Delay进行1ms的延迟,用当前缓存区剩余未播放的长度大于0结合前面的延迟进行等待。

步骤四(附加):回调函数
  开始播放后,会有音频其他子线程来调用回调函数,进行音频数据的补充,经过测试每次补充4096个字节。

步骤五:关闭音频设别
步骤六:退出SDL系统

源码

// 3-FFmpeg-SDL视频播放器.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <iomanip>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libswresample/swresample.h"
#include <libavutil/samplefmt.h>
#include "SDL/SDL.h"
#include "stdio.h"
}
#define PCM_BUFFER_SIZE	(1024*2*2*2)

static Uint8 *s_audio_buf = NULL;
static Uint8 *s_audio_pos = NULL;
static Uint8 *s_audio_end = NULL;
/*typedef SDL_AudioSpec{
	int freq;						//音频采样率
	SDL_AudioFormat format;			//音频格式
	uint8_t channels;				//声道数:1.单声道 2.双声道
	uint8_t silence;				//设置静音的值	声音采样是有符号的
	uint16_t sample;				//音频缓冲区中的采样个数,要求必须是2的n次
	uint16_t padding;				//考虑到兼容性的一个参数
	uint32_t size;					//音频缓冲区的大小,以字节为单位
	SDL_AudioCallback callback;		//填充音频缓冲区的回调函数
	void *userdate;					//用户自定义的数据
}SDL_AudioSpec;*/
void fill_audio_pcm(void *userdata, Uint8 * stream, int len){
	//SDL2中必须首先使用SDL_memset()将stream中的数据设置为0。
	SDL_memset(stream, 0, len);
	//std::cout << s_audio_pos << " " << s_audio_end << std::endl;
	if (s_audio_pos >= s_audio_end) {
		return;
	}
	int remain_buffer_len = s_audio_end - s_audio_pos;			
	len = (remain_buffer_len < len) ? remain_buffer_len : len;		//一般情况是4096 最后一帧的时候 有可能只有1900多 
	//拷贝数据到stream并调整音量
	SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);
	std::cout << "len = " << len << std::endl;
	s_audio_pos += len;
	return ;
}
#undef main
int main() {
	//创造SDL_AudioSpec结构体供SDL_OpenAudio打开音频
	SDL_AudioSpec spec;
	FILE *audio_fd = NULL;
	const char* filePath = "44100_16bit_2ch.pcm";
	size_t read_buffer_len = 0;
	int data_count = 0;
	//初始化SDL
	if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		std::cout << "Could not initial SDL - " << SDL_GetError() << std::endl;
		return -1;
	}
	audio_fd = fopen(filePath, "rb+");
	if (!audio_fd) {
		std::cout << "Could not oepn File" << std::endl;
		goto _FAIL;
	}
	spec.freq = 44100;
	spec.format = AUDIO_S16SYS;
	spec.channels = 2;
	spec.silence = 0;
	spec.samples = 1024;
	spec.callback = fill_audio_pcm;
	spec.userdata = NULL;
	s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);

	//打开音频设备
	if (SDL_OpenAudio(&spec, NULL)) {
		//goto _FAIL;
	}
	//播放
	SDL_PauseAudio(0);
	while(1){
		read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);
		if (read_buffer_len == 0) {
			break;
		}
		data_count += read_buffer_len;
		std::cout << "已经读取数据 :" << data_count << std::endl;
		s_audio_end = s_audio_buf + read_buffer_len;
		s_audio_pos = s_audio_buf;
		//std::cout << s_audio_pos << " " << s_audio_end << std::endl;
		while (s_audio_pos < s_audio_end) {
			SDL_Delay(10);
		}
	}
	std::cout << "finished" << std::endl;
	SDL_CloseAudio();
_FAIL:
	//退出SDL 
	if (audio_fd) {
		fclose(audio_fd);
	}
	if (s_audio_buf) {
		free(s_audio_buf);
	}
	SDL_Quit();
	return 0;
}

结果
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值