测试环境:window64 codeblock(mingw64)
下载libmad源码后发现自带的minimad.c是用了linux的API,所以修改了下,将
F:\d.mp3音乐文件解码后将pcm数据写入F:\d.pcm,并且用ffplay播放
一开始写入mp3全部的数据,minimad会发出decoding error 0x0101 (lost synchronization)等错误。
但是后面还是解码了,不明白为什么,所以去看了下mp3文件格式,发现mp3由标签头和音频数据帧组成
音频数据帧开头有11bit的同步字节,所以就明白了需要去掉标签头将后面的帧数据送入即可。
译码完成后用ffplay播放原生PCM数据
ffplay -f s16le -ac 2 -ar 44100 d.pcm
-f指定格式为有符号16bit pcm小端序 short 16 little endian (端序的话看你在输出回调里面怎么写)
-ac指定音频通道数audio channel(根据你的mp3文件的通道决定,用ffmpeg -i d.mp3就可以看)
-ar指定音频采样率44100Hz(根据你的mp3文件的采样率决定,用ffmpeg -i d.mp3就可以看)
结果如下:
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
# include "mad.h"
/**
libmad测试
**/
static int decode(unsigned char const *, unsigned long);
static FILE *fout;
//得到文件的大小
int file_size(char* filename)
{
FILE *fp=fopen(filename,"r");
if(!fp) return -1;
fseek(fp,0L,SEEK_END);
int size=ftell(fp);
fclose(fp);
return size;
}
int main()
{
char *fdm,*fdm2;
FILE *p;
char label[10];
int label_size;
int fileSize;
fileSize=file_size("F:\\d.mp3");
if(_access("F:\\d.pcm",F_OK)==0)remove("F:\\d.pcm");//如果存在就删除
fout=fopen("F:\\d.pcm","wb");
if(fout==NULL){
printf("create file failed\n");
return -1;
}
p=fopen("F:\\d.mp3","rb");
if(p==NULL){
printf("open file failed\n");
return -1;
}
if(fread(label,sizeof(char),10,p)>0){
//计算标签大小
label_size=((label[6]&0x7F)<<21)|((label[7]&0x7F)<<14)|((label[8]&0x7F)<<7)|((label[9]&0x7F));
label_size+=10;
printf("label size=%d\nfileSize=%d\n",label_size,fileSize);
//分配音频数据帧缓冲区
fdm2=fdm=malloc(fileSize-label_size);
if(fdm==NULL){
printf("malloc failed\n");
}
fread(fdm,sizeof(char),label_size-10,p);//去掉标签头数据,剩下的就是音频数据帧
}else return -1;
printf("read %d bytes\n",fread(fdm,sizeof(char),fileSize-label_size,p));
printf("decode ok %s\n",decode(fdm,fileSize-label_size)==0?"OK":"failed");
free(fdm2);
fclose(p);
fclose(fout);
return 0;
}
struct buffer {
unsigned char const *start;
unsigned long length;
};
/**
输入回调函数,一次性写入所有音频数据帧
**/
static
enum mad_flow input(void *data,
struct mad_stream *stream)
{
struct buffer *buffer = data;
if (!buffer->length)
return MAD_FLOW_STOP;
mad_stream_buffer(stream, buffer->start, buffer->length);
buffer->length = 0;
return MAD_FLOW_CONTINUE;
}
/*
转换24bit pcm为16bit pcm
*/
static inline
signed int scale(mad_fixed_t sample)
{
/* round */
sample += (1L << (MAD_F_FRACBITS - 16));
/* clip */
if (sample >= MAD_F_ONE)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
/* quantize */
return sample >> (MAD_F_FRACBITS + 1 - 16);
}
/*
输出回调函数,每译码完成一帧音频数据,就调用一次
*/
static
enum mad_flow output(void *data,
struct mad_header const *header,
struct mad_pcm *pcm)
{
unsigned int nchannels, nsamples;
mad_fixed_t const *left_ch, *right_ch;
unsigned char *buf;
int index;
nchannels = pcm->channels;
nsamples = pcm->length;
left_ch = pcm->samples[0];
right_ch = pcm->samples[1];
buf = malloc(nsamples*nchannels*2);
index=0;
while (nsamples--) {
signed int sample;
sample = scale(*left_ch++);
*(buf+2*nchannels*index+0)=(sample >> 0) & 0xff;
*(buf+2*nchannels*index+1)=(sample >> 8) & 0xff;
if (nchannels == 2) {
sample = scale(*right_ch++);
*(buf+2*nchannels*index+2)=(sample >> 0) & 0xff;
*(buf+2*nchannels*index+3)=(sample >> 8) & 0xff;
}
index++;
}
fwrite(buf,sizeof(char),pcm->length*nchannels*2,fout);
free(buf);
return MAD_FLOW_CONTINUE;
}
/*
错误回调函数,有错误就调用
*/
static
enum mad_flow error(void *data,
struct mad_stream *stream,
struct mad_frame *frame)
{
struct buffer *buffer = data;
fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
stream->error, mad_stream_errorstr(stream),
stream->this_frame - buffer->start);
return MAD_FLOW_CONTINUE;
}
/*
译码
*/
static
int decode(unsigned char const *start, unsigned long length)
{
struct buffer buffer;
struct mad_decoder decoder;
int result;
buffer.start = start;
buffer.length = length;
//初始化译码器,设置回调函数
mad_decoder_init(&decoder, &buffer,
input, 0 /* header */, 0 /* filter */, output,
error, 0 /* message */);
//运行译码器
result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
mad_decoder_finish(&decoder);
return result;
}