您的位置:首页 > 健康 > 养生 > 长春火车站咨询电话号码是多少_智慧团建pc版官网_推广的软件_seo优化行业

长春火车站咨询电话号码是多少_智慧团建pc版官网_推广的软件_seo优化行业

2025/4/23 3:30:53 来源:https://blog.csdn.net/Antonio915/article/details/147165433  浏览:    关键词:长春火车站咨询电话号码是多少_智慧团建pc版官网_推广的软件_seo优化行业
长春火车站咨询电话号码是多少_智慧团建pc版官网_推广的软件_seo优化行业

相关API

打开音频设备

int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained); 
  • desired:期望的参数。
  • obtained:实际音频设备的参数,一般情况下设置为NULL即可。
SDL_AudioSpec
typedef struct SDL_AudioSpec {
int freq; // 音频采样率
SDL_AudioFormat format; // 音频数据格式
Uint8 channels; // 声道数: 1 单声道, 2 立体声
Uint8 silence; // 设置静音的值,因为声音采样是有符号的,所以0当然就是这个值
Uint16 samples; // 音频缓冲区中的采样个数,要求必须是2的n次
Uint16 padding; // 考虑到兼容性的一个参数
Uint32 size; // 音频缓冲区的大小,以字节为单位
SDL_AudioCallback callback; // 填充音频缓冲区的回调函数
void *userdata; // 用户自定义的数据
} SDL_AudioSpec;

播放音频

回调函数
SDL_AudioCallback
// userdata:SDL_AudioSpec结构中的用户自定义数据,一般情况下可以不用。
// stream:该指针指向需要填充的音频缓冲区。
// len:音频缓冲区的大小(以字节为单位)1024*2*2。
void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 *stream, int len);

播放音频数据

// 当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候,将会播放静音的值
void SDLCALL SDL_PauseAudio(int pause_on)

实现流程

准备pcm文件

我们使用ffmpeg提取出pcm文件

ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm

将文件放在在构建路径

在这里插入图片描述

初始化SDL音频模块

因为要用到SDL音频模块,所以这里要初始化一下

if(SDL_Init(SDL_INIT_AUDIO))    // 支持AUDIO
{fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());return ret;
}

读取文件

使用C语言以二进制方式读取音频文件

FILE *audio_fd = NULL; 
const char *path = "44100_16bit_2ch.pcm";
audio_fd = fopen(path, "rb");
if(!audio_fd)
{fprintf(stderr, "Failed to open pcm file!\n");goto _FAIL;
}

设置音频缓冲区

每次从音频文件中拿取一部分音频,读取后再去拿取,设置缓冲区大小为1024*2*2*2字节
计算逻辑如下:

  • 每帧采样点为1024
  • 音频通道数为2
  • 音频采样位深为16bit = 2byte,即2字节
  • 每次缓存2帧数据
    因此缓冲区大小为:1024*2*2*2 = 8196字节
#define PCM_BUFFER_SIZE (1024*2*2*2)
为音频缓冲区分配内存

这里使用的数据类型为uint8_t,即8位无符号整数

static uint8_t* s_audio_buf = NULL;
s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);

设置音频格式

设置SDL中音频相关接口的参数

  • 采样频率(sample rate)
  • 采样格式(format)
  • 通道数(channels)
  • 采样个数(samples)

设置其他字段:

  • 静音基准值
  • 对齐格式,不设置默认为0
  • 回调函数,用于数据读取完毕后的操作
  • 回调函数参数,如果是全局参数可以忽略
SDL_AudioSpec spec;
spec.freq = 44100;          // 采样频率
spec.format = AUDIO_S16SYS; // 采样点格式
spec.channels = 2;          // 2通道
spec.silence = 0;          //静音
spec.samples = 1024;       //采样点 
spec.callback = fill_audio_pcm; // 回调函数
spec.userdata = NULL; //回调函数参数

设置回调函数

SDL需要填充数据的时候,就会调用这个回调函数,因此我们需要在这个函数中填充数据。

  • udata之前设置为NULL,因此可以忽略不计
  • stream是需要被填充的地址
  • len是需要填充的大小

因此,我们需要先清空stream内部数据,然后将文件中读取到的数据拷贝到stream

  • SDL_MixAudio用于拷贝数据到stream,并且设置SDL_MIX_MAXVOLUME宏用于设置音量大小

在回调函数中更新读取位置信息,触发主线程循环中的再次读取信息

// 目前读取的位置
static Uint8 *s_audio_pos = NULL;
// 缓存结束位置
static Uint8 *s_audio_end = NULL;void fill_audio_pcm(void *udata, Uint8 *stream, int len)
{SDL_memset(stream, 0, len);if(s_audio_pos >= s_audio_end) // 数据读取完毕{return;}// 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少)int remain_buffer_len = s_audio_end - s_audio_pos;len = (len < remain_buffer_len) ? len : remain_buffer_len;// 拷贝数据到stream并调整音量SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME);printf("len = %d\n", len);s_audio_pos += len;  // 移动缓存指针
}

循环读取音频数据

设置SDL读取文件后直接开始读取数据,触发回调机制

SDL_PauseAudio(0);

循环读取文件片段,更新文件的起始位置和结束为止,直到文件读取完毕后退出循环

// 目前读取的位置
static Uint8 *s_audio_pos = NULL;
// 缓存结束位置
static Uint8 *s_audio_end = NULL;while(1){// 从文件读取PCM数据read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);if(read_buffer_len == 0){break;}data_count += read_buffer_len; // 统计读取的数据总字节数printf("now playing %10d bytes data.\n",data_count);s_audio_end = s_audio_buf + read_buffer_len;    // 更新buffer的结束位置s_audio_pos = s_audio_buf;  // 更新buffer的起始位置//the main thread wait for a momentwhile(s_audio_pos < s_audio_end){SDL_Delay(10);  // 等待PCM数据消耗}}

需要计算每次回调的时间,目的是让出时间给CPU休眠,因为只有回调函数结束的时候,才会满足s_audio_pos < s_audio_end
得出的时间为:1024 / 44.1k = 0.23ms,可以设置短一点,这里就设置为10ms,休眠后检查是否需要再次读取文件数据即可。

关闭设备和清空内存

播放结束后,需要关闭SDL音频设备和音频文件,并且退出SDL子系统

SDL_CloseAudio();if(s_audio_buf)free(s_audio_buf);if(audio_fd)fclose(audio_fd);SDL_Quit();

完整代码

main.c

/*** SDL2播放PCM** Darren 326873713* 326873713@qq.com* 腾讯课堂-零声学院:https://ke.qq.com/course/468797** 本程序使用SDL2播放PCM音频采样数据。SDL实际上是对底层绘图* API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层* API。* 测试的PCM数据采用采样率44.1k, 采用精度S16SYS, 通道数2** 函数调用步骤如下:** [初始化]* SDL_Init(): 初始化SDL。* SDL_OpenAudio(): 根据参数(存储于SDL_AudioSpec)打开音频设备。* SDL_PauseAudio(): 播放音频数据。** [循环播放数据]* SDL_Delay(): 延时等待播放完成。**/#include <stdio.h>
#include <SDL.h>
#include<stdlib.h>
#include <unistd.h>// 每次读取2帧数据, 以1024个采样点一帧 2通道 16bit(2字节)采样点为例
#define PCM_BUFFER_SIZE (1024*2*2*2)// 音频PCM数据缓存
static Uint8 *s_audio_buf = NULL;
// 目前读取的位置
static Uint8 *s_audio_pos = NULL;
// 缓存结束位置
static Uint8 *s_audio_end = NULL;//音频设备回调函数
void fill_audio_pcm(void *udata, Uint8 *stream, int len)
{SDL_memset(stream, 0, len);if(s_audio_pos >= s_audio_end) // 数据读取完毕{return;}// 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少)int remain_buffer_len = s_audio_end - s_audio_pos;len = (len < remain_buffer_len) ? len : remain_buffer_len;// 拷贝数据到stream并调整音量SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME);printf("len = %d\n", len);s_audio_pos += len;  // 移动缓存指针
}// 提取PCM文件
// ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
// 测试PCM文件
// ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
#undef main
int main(int argc, char *argv[])
{int ret = -1;FILE *audio_fd = NULL;SDL_AudioSpec spec;const char *path = "44100_16bit_2ch.pcm";// 每次缓存的长度size_t read_buffer_len = 0;//SDL initializeif(SDL_Init(SDL_INIT_AUDIO))    // 支持AUDIO{fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());return ret;}//打开PCM文件audio_fd = fopen(path, "rb");if(!audio_fd){fprintf(stderr, "Failed to open pcm file!\n");goto _FAIL;}s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);// 音频参数设置SDL_AudioSpecspec.freq = 44100;          // 采样频率spec.format = AUDIO_S16SYS; // 采样点格式spec.channels = 2;          // 2通道spec.silence = 0;spec.samples = 1024;       // 23.2ms -> 46.4ms 每次读取的采样数量,多久产生一次回调和 samplesspec.callback = fill_audio_pcm; // 回调函数spec.userdata = NULL;//打开音频设备if(SDL_OpenAudio(&spec, NULL)){fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());goto _FAIL;}//play audioSDL_PauseAudio(0);int data_count = 0;while(1){// 从文件读取PCM数据read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);if(read_buffer_len == 0){break;}data_count += read_buffer_len; // 统计读取的数据总字节数printf("now playing %10d bytes data.\n",data_count);s_audio_end = s_audio_buf + read_buffer_len;    // 更新buffer的结束位置s_audio_pos = s_audio_buf;  // 更新buffer的起始位置//the main thread wait for a momentwhile(s_audio_pos < s_audio_end){SDL_Delay(10);  // 等待PCM数据消耗}}printf("play PCM finish\n");// 关闭音频设备SDL_CloseAudio();_FAIL://release some resourcesif(s_audio_buf)free(s_audio_buf);if(audio_fd)fclose(audio_fd);//quit SDLSDL_Quit();return 0;
}

更多资料

更多资料参考:https://github.com/0voice

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com