音频信号
音频信号(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;
}
结果