tinymp3
tinymp3 mp3的编解码器官方demo,将mp3或者wav文件解码后重新压缩为mp3。用法:
运行截图
文件说明
-
dr_mp3.h MP3文件解码与保存库
-
dr_wav.h WAV文件解码与保存库
-
shine_mp3.h 编码器头文件
-
shine_mp3.c 编码器库
使用流程
编码器部分
初始化
- 不同PCM采样率对应的算法
所使用的算法 | PCM采样率1 | PCM采样率2 | PCM采样率3 |
---|---|---|---|
MPEG-I | 44100 | 48000 | 32000 |
MPEG-II | 22050 | 24000 | 16000 |
MPEG-2.5 | 11025 | 12000 | 8000 |
- 不同算法所支持的比特率
算法名字 | MPEG-2.5 | MPEG-II | MPEG-I |
---|---|---|---|
1 | -1 | -1 | -1 |
2 | 8 | 8 | 32 |
3 | 16 | 16 | 40 |
4 | 24 | 24 | 48 |
5 | 32 | 32 | 56 |
6 | 40 | 40 | 64 |
7 | 48 | 48 | 80 |
8 | 56 | 56 | 96 |
9 | 64 | 64 | 112 |
10 | 80 | 128 | |
11 | 96 | 160 | |
12 | 112 | 192 | |
13 | 128 | 224 | |
14 | 144 | 256 | |
15 | 160 | 320 |
- 初始化参考代码
//公共变量
shine_config_t config;//编码器配置
shine_t shine;//编码器句柄
modes stereo = STEREO;//编码模式
//--
set_defaults(&config);//配置复位
stereo = STEREO;//设置模式
config.mpeg.bitr = 128;//设置码率
outfname="out.mp3";//设置输出的文件名(编码器不需要使用)
config.wave.samplerate = 44100;//设置PCM流的采样率
config.wave.channels = PCM_STEREO;//这事PCM流模式
/* See if samplerate and bitrate are valid */
if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0){//检测转换是否支持
qDebug()<<"Unsupported samplerate/bitrate configuration.";
return ;
}
outfile = fopen(outfname, "wb");//打开文件
if (!outfile) {
qDebug()<<"Could not create \"%s\".\n"+ QString(outfname);
return;
}
/* Set to stereo mode if wave data is stereo, mono otherwise. */
if (config.wave.channels > 1)
config.mpeg.mode = STEREO;//设置音频模式
else
config.mpeg.mode = MONO;
/* Initiate encoder */
shine = shine_initialise(&config);//初始化编码器
written=0;
CodecState=1;//标志编码器启动
m_audioEncodeData.clear();
数据编码
- 数据编码代码
if(CodecState){//编码器是否初始化
m_audioEncodeData.append(audioData);//将audioData里面的PCM数据累加到m_audioEncodeData中
int samples_per_pass = shine_samples_per_pass(shine) * PCM_STEREO;//计算一次编码所需要的数据,uint16为单位
for(;m_audioEncodeData.size()>samples_per_pass*2;){//循环编码
data = shine_encode_buffer_interleaved(shine, (int16_t *)m_audioEncodeData.data(), &written);//编码
if (write_mp3(written, data, &config) != written) {//数据写入mp3文件中
qDebug()<<"mp3 encoder && decoder: write error";
}
m_audioEncodeData.remove(0,samples_per_pass*2);//将本次数据弹出
}
}
- 编码流程
结束编码
- 结束编码使用
unsigned char *data;
CodecState=0; //标记编码器结束
/* Flush and write remaining data. */
data = shine_flush(shine, &written); //产生mp3尾部数据
write_mp3(written, data, &config);//写入尾部数据
/* Close encoder. */
shine_close(shine);//关闭编码器
/* Close the MP3 file */
fclose(outfile);//关闭文件
解码器部分
初始化
- 初始化代码
drmp3 mp3; // 全局变量
if (!drmp3_init_file(&mp3, "out.mp3", NULL)) {//打开文件
qDebug()<<"文件打开失败";
return;
}
timep->start(10);//10ms的播放定时器启动,10ms为播放队列填充pcm数据保证播放的连续性(一次性解码无需使用)
数据解码
- 一次性解码
uint64_t totalSampleCount = 0; //返回的样本总数
int16_t *data_in = drmp3__full_read_and_close_s16(&mp3, NULL, &totalSampleCount);//一次性解码并返回PCM数据
if (data_in == NULL){
qDebug()<<"解码失败";
}
QFile file("rawMp3");
file.open(QIODevice::WriteOnly);
file.write((char *)data_in,totalSampleCount);
file.close();
qDebug()<<"保存成功";
return;
- 分块解码
//解码逻辑
if(pcm1.size()<4096){//pcm1 为MP3解码缓冲队列
drmp3_int16 temp[4096];//用于保存解码数据的缓冲
drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / mp3.channels;//计算本次解码的PCM数据量
//进行一次解码(每次解码解码器会自动往前移动)
drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(&mp3, framesToReadRightNow, temp);
//将缓冲数据压入MP3解码缓冲队列,注:此处使用了Qt测试平台。
pcm1.append(QByteArray((char *)&temp[0],framesJustRead*mp3.channels*sizeof(drmp3_int16)));
if(framesJustRead==0){
stopDecode();
return;
}
}
//播放逻辑,以下代码与解码无关,是Qt的音频播放逻辑,可参考使用
if (pcm1.size() > 0) {//当缓冲有数据时候
int readSize = m_audioOutput->periodSize();//获取播放器单次播放所需数据量
int chunks = m_audioOutput->bytesFree() / readSize;//获取当前播放器能存入的空白帧
while (chunks) {
QByteArray samples = pcm1.mid(0, readSize);//将所需数据放入缓冲变量
int len = samples.size();
pcm1.remove(0, len);//弹出已使用的数据
if (len) updateAudioData(samples);//写入播放器
if (len != readSize) break;
chunks--;
}
}
结束解码
- 结束代码
qDebug()<<"结束解码";
timep->stop();//关闭定时器
drmp3_uninit(&mp3);//关闭解码器
playFile->close();//关闭播放文件
mp3库地址
https://github.com/cpuimage/tinymp3