• 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 
    #include 
    extern "C" {
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libswscale/swscale.h"
    #include "libavutil/imgutils.h"
    #include "libswresample/swresample.h"
    #include 
    #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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    结果
    在这里插入图片描述

  • 相关阅读:
    基于混沌搜索策略的鲸鱼优化算法-附代码
    设计模式:工程模式
    认识wireshark
    一文详解JVM的内存结构
    K8S 集群搭建
    【Qt】常用控件(一)
    Django 内置的过滤器与标签、自定义过滤器与标签
    手搭手Ajax实现搜索地址自动补全功能
    基于S32K144驱动NSD8308
    ef core code first pgsql
  • 原文地址:https://blog.csdn.net/beijingFC/article/details/126192164