原创 让我们做一个MP3 PLAYER吧!

2010-4-24 16:17 6989 8 8 分类: MCU/ 嵌入式
让我们做一个Mp3吧!
by panasonic.lin@163.com

前记:续wav音乐播放器的bug
1.//注册设备
static int __init init_sound(void)
{
...
    /* And the output buffer. */
    kfifo_init(&sound_fifo,&SoundWrite_buf, BUF_SIZE);
   
    /* And the output info */
    sound_output_active = 0;
    spin_lock_init (&sound_out_lock);
  
    /* Fill the sound_task structure, used for the bottom half handler. */
    sound_task.routine = sound_do_task;
    sound_task.data = NULL; /* unused */

   
    /* And the output info */
    sound_output_active = 0;
    spin_lock_init (&sound_out_lock);
   
    /* Fill the sound_task structure, used for the bottom half handler. */
    sound_task.routine = sound_do_task;
    sound_task.data = NULL; /* unused */
 上次发布的wav音乐播放器的驱动有个bug,只能打开设备一次,第二次打开就没反应了,原因在于初始化的数据结构的函数放在
了init_sound只在注册模块的时候调用一次,所以在没有卸载模块之前,第二次打开设备使得程序在不确定状态。解决方法是把上面
这些初始化数据结构的函数放在dev_open函数就可以拉。
static int dev_open(struct inode *inode,struct file *filp)
修改好uda1314.c的源码后,简单的make就可以生成模块uda1314.o备用。

一点牢骚:
在这之前,我已经把libmad和madlld编译修改好,初步调试,可以正常解码mp3,文档和翻译我都记录下来了,准备共享给大家。开会回来,电脑黑屏,重新启动,opensuse提示找不到/bin/init,用opensuse的dvd修复系统时候才发现,硬盘分区表包括MBR都
坏了!修复也没用,ext4格式的分区让我无从下手,眼睁睁的看着数据丢失,一切重头来过。所谓同行业质保最长的三心硬盘又一次的
伤害了我幼小的心灵!但是讽刺的是这块arm的板子就是三心的cpu!

准备之前,参考uboot移植的文章,往板子上面移植个正常的uboot上去,然后再编译一个正常的uclinux核心,这些在44b0平台可以
参考我之前的博客文章。这里说些重要的,音频设备最重要的就是采样率,采样率跟时钟频率相关。我这里只实验了16khz的采样率,采样位数16bit,立体声。
//IIS master初始化,MCLK=60mHz,60,000,000/256/16=14,649Hz=fs?
    //14,649*2/16=1831Hz=serial bit clock
    //SCLK的频率=通(声)道数×采样频率×采样位数
    /*LRCK的频率等于采样频率*/
  
    rIISCON = 0x22; //使能Tx DMA,disable receive DMA,IISLRCK(in Tx and Rx generate),使能预分频器,iis停止
    rIISMOD = 0x89; //主模式,发送方式,iis格式,16bit/通道,codeclk = 256fs,Serial bit clock = 32fs
    rIISPSR = 0x77; //使能预分频器a/b enable division factor=16,so,MCLK/16=256fs->fs=65.536M/256*16=16khz
    rIISFCON = 0xa00; //Tx DMA access mode,RX Normal access,enable TX FIFO
    //使能IIS
    rIISCON |= 0x1;


由iis的采样率反推cpu的主频大概是65.536M,约等于66Mhz。uboot和uclinux都应该设定一样的cpu主频!由于之前我板子上面的uboot是66M的主频,我在编译uclinux的时候选择的主频是60M,导致启动uclinux的时候start kernel后控制台全是乱码。
点击看大图
uboot的主频设定比较简单,在/cpu/s3c44b0/start.S可以设定pllcon寄存器,另外在/driver/serial/找到
44b0串口的代码,修改相应主频下的波特率分频因子,最后在/include/configs配置文件配置主频==66M。
uclinux在make menuconfig的时候并没有主频可以填写,而早uclinux的其他文件提到配置cpu主频的很多,到底那个才是有效的?不得而知。
1是在/vendors/samsung/44b0/config.linux-2.4.x
# CONFIG_CPU_WITH_MCR_INSTRUCTION is not set
CONFIG_ARM_CLK=66000000
CONFIG_SERIAL_S3C44B0X=y
2是在/linux2.4.x/arch/armnommu/config.in,这个应该是配置默认值
if [ "$CONFIG_BOARD_MBA44" = "y" ]; then
    define_string CONFIG_SPU_NAME "S3C44B0X"
    define_bool CONFIG_CPU_S3C44B0X         y
    define_bool CONFIG_CPU_ARM710           y
    define_bool CONFIG_CPU_32v4             y
    define_bool CONFIG_CPU_32               y
    define_bool CONFIG_CPU_26               n
    define_bool CONFIG_NO_PGT_CACHE         y
    define_bool CONFIG_CPU_WITH_CACHE       y
    define_bool CONFIG_CPU_WITH_MCR_INSTRUCTION n
    define_int  CONFIG_ARM_CLK              66000000
    define_bool CONFIG_SERIAL_S3C44B0X      y
不过怎么样,最后的配置config后都生成/linux2.4.x/include/linux/autoconf.h头文件,它被编译到系统(很多文件都包含这个头文件),所以以这个为准。
#define CONFIG_CPU_WITH_CACHE 1
#undef  CONFIG_CPU_WITH_MCR_INSTRUCTION
#define CONFIG_ARM_CLK (66000000)
#define CONFIG_SERIAL_S3C44B0X 1

好,环境都准备好了,最后最好弄个nfs的开发环境,修改文件比较方便。另外根文件系统的busybox应该有常用的程序如cat,insmod,lsmod,rmmod,echo,mount,和shell。最后用mknod在根文件系统的/dev目录下建立一个设备文件iis,主
编号iis_major=14,次编号0,名字是iis,不管怎么样,目标一个:跟下面的函数保持一致。
result = register_chrdev(iis_major,DEV_IIS,&sound_dev_ops);

下一步是把编译好的模块uda1314.o拷贝到根文件系统,然后插入到核心中,
#insmod ./uda1314.o
可以看到如下提示
点击看大图

#lsmod 可以看到当前活动的模块
mp3播放的基础就是raw或者说wav格式的数据能直接播放的话,那基于这层的mp3解码,只是把mp3解码成pcm的bit流,然后往这个
device写就是了,从这个意义上说,mp3播放软件是应用软件,靠的就是底层核心态的pcm驱动!
我们可以往设备写pcm的wave文件检验驱动的正确性。写入之前确保dream这个wave文件是16k采样,16bit,立体声的格式,可以通过cooledit或者windows的录音机来转换格式。
#cat ./dream.wav > /dev/iis

驱动正常工作,开始mp3解码成pcm的工作。用libmad的有两种方法,一种是简单的利用高层的api写,另外一种是用底层的api,下面两种方法都会介绍到。
 下载libmad和madlld。
第一步是编译libmad库:libmad.a和mad.h,后续的mp3应用程序需要链接到这个库。
./configure --help获得可用的配置选项。
panasonic@linux-p5tw:~/soundcard/libmad-0.15.1b> ./configure --help
`configure' configures MPEG Audio Decoder 0.15.1b to adapt to many kinds of systems.

Usage: ./configure [OPTION]... [VAR=VALUE]...

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.

Configuration:
  -h, --help              display this help and exit
      --help=short        display options specific to this package
      --help=recursive    display the short help of all the included packages
  -V, --version           display version information and exit
  -q, --quiet, --silent   do not print `checking...' messages
      --cache-file=FILE   cache test results in FILE [disabled]
  -C, --config-cache      alias for `--cache-file=config.cache'
  -n, --no-create         do not create output files
      --srcdir=DIR        find the sources in DIR [configure dir or `..']

Installation directories:
注意在正式congfigure之前,由于arm-elf-gcc的特殊性,需要export一些变量,否则,真的很难解决这个问题:问题是./configure会报告说
arm-elf-gcc没有能力生成elf的执行文件。对了,arm-elf-gcc需要-elf2flt这个CFLAGS!
#export CC=arm-elf-gcc CFLAGS=-Wl,-elf2flt

panasonic@linux-p5tw:~/soundcard/libmad-0.15.1b> ./configure --enable-fpm=arm --host=arm-elf --disable-debugging --prefix=/home/panasonic/soundcard/libmad-0.15.1b/install --disable-shared --enable-static --enable-speed
configure: WARNING: If you wanted to set the --build type, don't use --host.
    If a cross compiler is detected then cross compile mode will be used.
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for arm-elf-strip... arm-elf-strip

make TARGET_ARCH=arm CROSS=arm-elf-
make install
最后生成mad.h和libmad.a静态库。

有了libmad.a库,下面写个简单的应用软件,这是用libmad高层的api写,比较简单,其实就是来自libmad自带的minimad.c
,test_music.c的基础就是前面介绍的wav播放器,所以
这个程序也可以播放wav音乐文件,判断命令行传递的argv参数就知道是mp3文件还是wav文件。
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <string.h>
//add for mp3 play
# include <sys/stat.h>
# include <sys/mman.h>
# include <sys/types.h>
# include "./mad.h"


#define BUF_SIZE 16*1024
#define DEVICE_NAME "/dev/iis"


static int sfd;            /*声音设备的描述符 */

static int decode(unsigned char const *, unsigned long);

void delay(long x)
{   
    unsigned long i;   
    for(i=0; i<x; i++);
}


struct buffer {
    unsigned char const *start;
    unsigned long length;
};


int main(int argc, char *argv[])
{
    int i=0;   
    struct stat stat;
   void *fdm;
    FILE *file_fd;    //文件
    int fd;
long file_len;    //文件长度
long loops;
    int fread_ret;

    unsigned char audio_buffer[BUF_SIZE];
    unsigned char *file_name;

    printf("This is a wav&mp3 play program.\n");
    printf("Please add a file name and the audio rate!\nsuch as \"./test_music test.mp3\" \n\n\n");
    delay(100000);   
       
    file_name = argv[1];
    file_len = strlen(file_name);
   
    if(file_len < 3)
    {
        printf("file is too short!\n");
        exit(0);
    }           
   
    printf("File name : %s \n",file_name);
    delay(100000);
//if file format is mp3//
if(file_name[file_len-3]=='m' && file_name[file_len-2]=='p' && file_name[file_len-1]=='3')
{
        printf("Play mp3!\n");

        fd = open(file_name, O_RDONLY);

        if ((sfd = open("/dev/iis", O_WRONLY)) < 0)
    {
        printf("can not open device!!!\n");
        return 5;
        }


        if (fstat(fd, &stat) == -1 || stat.st_size == 0)
        return 2;

        fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);

        if (fdm == MAP_FAILED)
        return 3;

        decode(fdm, stat.st_size);

        if (munmap(fdm, stat.st_size) == -1)
        return 4;

        close(sfd);

}
else
{
    /*打开音频设备,准备play*/   
    if ((sfd = open(DEVICE_NAME, O_WRONLY)) == -1)
    {
        printf("open error\n");   
        return -1;
    }

    /*打开并计算文件长度*/
    file_fd = fopen(file_name, "r");
    fseek(file_fd,0,SEEK_END);     //定位到文件末 
    file_len = ftell(file_fd);     //文件长度

    loops = file_len/(BUF_SIZE);
   
    /*重新定位到文件头*/
    fclose(file_fd);
    file_fd = fopen(file_name, "r");
    /*播放wav文件*/
    for(i=0;i<loops;i++)
    {
        fread_ret=fread(audio_buffer, BUF_SIZE, 1, file_fd);
        //printf("fread_ret=%d,loops=%d,i=%d\n",fread_ret,loops,i);   
        write(sfd,audio_buffer,BUF_SIZE);
    }
    /*关闭设备和文件*/
    fclose(file_fd);
    close(sfd);
}
    return 0;

}


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;
}

/*这一段是处理采样后的pcm音频 */
static inline signed int scale(mad_fixed_t sample)
{

    sample += (1L << (MAD_F_FRACBITS - 16));

    if (sample >= MAD_F_ONE)
    sample = MAD_F_ONE - 1;
    else if (sample < -MAD_F_ONE)
    sample = -MAD_F_ONE;

    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, n;
    mad_fixed_t const *left_ch, *right_ch;
    unsigned char Output[6912], *OutputPtr;

    int fmt, wrote, speed;


    nchannels = pcm->channels;
    n = nsamples = pcm->length;
    left_ch = pcm->samples[0];
    right_ch = pcm->samples[1];


    //fmt = AFMT_S16_LE;
    //speed = pcm->samplerate * 2;    /*播放速度是采样率的两倍 */
    //ioctl(sfd, SNDCTL_DSP_SPEED, &(speed));
    //ioctl(sfd, SNDCTL_DSP_SETFMT, &fmt);
    //ioctl(sfd, SNDCTL_DSP_CHANNELS, &(pcm->channels));

    OutputPtr = Output;

    while (nsamples--) {
    signed int sample;

    sample = scale(*left_ch++);
    *(OutputPtr++) = sample >> 0;
    *(OutputPtr++) = sample >> 8;

    if (nchannels == 2) {
        sample = scale(*right_ch++);
        *(OutputPtr++) = sample >> 0;
        *(OutputPtr++) = sample >> 8;
    }
    }
    n *= 4;            /*数据长度为pcm音频采样的4倍 */
    OutputPtr = Output;

    while (n) {
    wrote = write(sfd, OutputPtr, n);
    OutputPtr += wrote;
    n -= wrote;

    }
    OutputPtr = Output;
    return MAD_FLOW_CONTINUE;
}


static enum mad_flow error(void *data,
            struct mad_stream *stream, struct mad_frame *frame)
{
    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, 0, output, error, 0);

    mad_decoder_options(&decoder, 0);

    result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

    mad_decoder_finish(&decoder);

    return result;
}


编译生成flt格式的程序,放置到根文件系统目录的bin下面。
panasonic@linux-p5tw:~/soundcard/uda1314> arm-elf-gcc -static -Wl,-elf2flt -o test_music test_music.c ./libmad.a
用cooledit转换一下mp3文件为16k的采样率,生成wav文件和相应的mp3文件,也一起放置在bin目录下。
点击看大图

测试结果表明,wav文件播放没有问题,mp3文件的大小如果大于1M的话,uclinux会报告内存不够,但是查看系统内存显示内存还有6M大呢!mmap内存映射是要申请内存的。
但是uclinux默认的可申请的内存在slab.c里面指定了,这可以在编译uclinux的时候改,但是也不是办法吧,用于学习可以,实际用途中,如果一个mp3文件很大很大,那
不是要申请好多内存。
点击看大图


不过这种方法的好处就是高速,一切都是内存读写,连续解码,实际试听也是,播放mp3很流畅。
播放wav文件时候,播放结束后,又遇到另外一个bug,就是歌曲播放结束了,但是程序还是拼命的往iis驱动write数据,结果导致耳机里面像火车一样的声音呜呜的响,我在驱动程序里面找了半天,后来在应用程序test_music的fread(audio_buffer, BUF_SIZE, 1, file_fd)函数打印的loops数字找到了答案,loops太大了!
ftell(file_fd)函数返回long类型的数值,loops应该是file_len除BUF_SIZE溢出了吧,最后定睛一看BUF_SIZE的宏,少了括号!导致除于16后乘1024了!

    /*打开并计算文件长度*/
    file_fd = fopen(file_name, "r");
    fseek(file_fd,0,SEEK_END);     //定位到文件末 
    file_len = ftell(file_fd);     //文件长度

    loops = file_len/(BUF_SIZE);
   
    /*重新定位到文件头*/
    fclose(file_fd);
    file_fd = fopen(file_name, "r");
    /*播放wav文件*/
    for(i=0;i<loops;i++)
    {
        fread_ret=fread(audio_buffer, BUF_SIZE, 1, file_fd);
        //printf("fread_ret=%d,loops=%d,i=%d\n",fread_ret,loops,i);   
        write(sfd,audio_buffer,BUF_SIZE);
    }
fread_ret=1,loops=988845056,i=942
                                                                                                                                                                                                                                                                                         
                                                                                                                                                   
#define BUF_SIZE 16*1024
<1>.fread_ret=1,loops=943,i=941                                                                                                                                                 
<1>.fread_ret=1,loops=943,i=942                                                                                                                                                 
<1>.<4>enter release function..                                                                                                                                                 
.<1>.<1>@<4>exit IISClose..


解码mp3基本上就是这样了^_^为了解决上面的问题,我想更进一步,弄个更实用的。
想了解libmad多一点,最好使用libmad的底层api,这个竟然没有文档!用linux世界的话说就是:一切都在代码里面。
madlld就是libmad的文档工程,使用libmad底层的api写的解码mp3的程序,由stdin输入,解码后写入到stdout。
madlld只有三个c文件,核心是madlld的MpegAudioDecoder(FILE *InputFp)函数。
本来这三个c文件我是翻译好的了,但是。。。。。。哎呀,硬盘没了,大家将就点吧,啃点E文对大家都有好处。
长篇大论我就说了,我只附上我对madlld.c的修改patch补丁。
https://static.assets-stash.eet-china.com/album/old-resources/2010/4/24/c7b73f03-e863-4502-9cf6-9071fe329c82.zip
为了我的附件不丢失,我还是把代码贴上来吧,免得到时候附件没了,还可以从博文里面复制麻。
/* HTAB = 4 */
/****************************************************************************
 * madlld.c -- A simple program decoding an mpeg audio stream to 16-bit        *
 * PCM from stdin to stdout. This program is just a simple sample            *
 * demonstrating how the low-level libmad API can be used.                    *
 *--------------------------------------------------------------------------*
 * (c) 2001--2004 Bertrand Petit                                            *
 *                                                                            *
 * Redistribution and use in source and binary forms, with or without        *
 * modification, are permitted provided that the following conditions        *
 * are met:                                                                    *
 *                                                                            *
 * 1. Redistributions of source code must retain the above copyright        *
 *    notice, this list of conditions and the following disclaimer.            *
 *                                                                            *
 * 2. Redistributions in binary form must reproduce the above                *
 *    copyright notice, this list of conditions and the following            *
 *    disclaimer in the documentation and/or other materials provided        *
 *    with the distribution.                                                *
 *                                                                             *
 * 3. Neither the name of the author nor the names of its contributors        *
 *    may be used to endorse or promote products derived from this            *
 *    software without specific prior written permission.                    *
 *                                                                             *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''        *
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED        *
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A            *
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR        *
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,                *
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT            *
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF            *
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND        *
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,        *
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT        *
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF        *
 * SUCH DAMAGE.                                                                *
 *                                                                            *
 ****************************************************************************/

/*
 * $Name: v1_1 $
 * $Date: 2004/02/22 03:27:05 $
 * $Revision: 1.18 $
 */

/****************************************************************************
 * Includes                                                                    *
 ****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <math.h> /* for pow() and log10() */
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
//add for mp3 play
# include <sys/stat.h>
# include <sys/mman.h>
# include <sys/types.h>
# include "./mad.h"
#include "bstdfile.h"

#define BUF_SIZE 16*1024
#define DEVICE_NAME "/dev/iis"


static int sfd;            /*声音设备的描述符 */


/****************************************************************************
 * Global variables.                                                        *
 ****************************************************************************/

/* Keeps a pointer to the program invocation name for the error
 * messages.
 */
const char    *ProgName;


/****************************************************************************
 * Return an error string associated with a mad error code.                    *
 ****************************************************************************/
/* Mad version 0.14.2b introduced the mad_stream_errorstr() function.
 * For previous library versions a replacement is provided below.
 */
#if (MAD_VERSION_MAJOR>=1) || \
    ((MAD_VERSION_MAJOR==0) && \
     (((MAD_VERSION_MINOR==14) && \
       (MAD_VERSION_PATCH>=2)) || \
      (MAD_VERSION_MINOR>14)))
#define MadErrorString(x) mad_stream_errorstr(x)
#else
static const char *MadErrorString(const struct mad_stream *Stream)
{
    switch(Stream->error)
    {
        /* Generic unrecoverable errors. */
        case MAD_ERROR_BUFLEN:
            return("input buffer too small (or EOF)");
        case MAD_ERROR_BUFPTR:
            return("invalid (null) buffer pointer");
        case MAD_ERROR_NOMEM:
            return("not enough memory");

        /* Frame header related unrecoverable errors. */
        case MAD_ERROR_LOSTSYNC:
            return("lost synchronization");
        case MAD_ERROR_BADLAYER:
            return("reserved header layer value");
        case MAD_ERROR_BADBITRATE:
            return("forbidden bitrate value");
        case MAD_ERROR_BADSAMPLERATE:
            return("reserved sample frequency value");
        case MAD_ERROR_BADEMPHASIS:
            return("reserved emphasis value");

        /* Recoverable errors */
        case MAD_ERROR_BADCRC:
            return("CRC check failed");
        case MAD_ERROR_BADBITALLOC:
            return("forbidden bit allocation value");
        case MAD_ERROR_BADSCALEFACTOR:
            return("bad scalefactor index");
        case MAD_ERROR_BADFRAMELEN:
            return("bad frame length");
        case MAD_ERROR_BADBIGVALUES:
            return("bad big_values count");
        case MAD_ERROR_BADBLOCKTYPE:
            return("reserved block_type");
        case MAD_ERROR_BADSCFSI:
            return("bad scalefactor selection info");
        case MAD_ERROR_BADDATAPTR:
            return("bad main_data_begin pointer");
        case MAD_ERROR_BADPART3LEN:
            return("bad audio data length");
        case MAD_ERROR_BADHUFFTABLE:
            return("bad Huffman table select");
        case MAD_ERROR_BADHUFFDATA:
            return("Huffman data overrun");
        case MAD_ERROR_BADSTEREO:
            return("incompatible block_type for JS");

        /* Unknown error. This swich may be out of sync with libmad's
         * defined error codes.
         */
        default:
            return("Unknown error code");
    }
}
#endif

/****************************************************************************
 * Converts a sample from mad's fixed point number format to a signed        *
 * short (16 bits).                                                            *
 ****************************************************************************/
static signed short MadFixedToSshort(mad_fixed_t Fixed)
{
    /* A fixed point number is formed of the following bit pattern:
     *
     * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
     * MSB                          LSB
     * S ==> Sign (0 is positive, 1 is negative)
     * W ==> Whole part bits
     * F ==> Fractional part bits
     *
     * This pattern contains MAD_F_FRACBITS fractional bits, one
     * should alway use this macro when working on the bits of a fixed
     * point number. It is not guaranteed to be constant over the
     * different platforms supported by libmad.
     *
     * The signed short value is formed, after clipping, by the least
     * significant whole part bit, followed by the 15 most significant
     * fractional part bits. Warning: this is a quick and dirty way to
     * compute the 16-bit number, madplay includes much better
     * algorithms.
     */

    /* Clipping */
    if(Fixed>=MAD_F_ONE)
        return(SHRT_MAX);
    if(Fixed<=-MAD_F_ONE)
        return(-SHRT_MAX);

    /* Conversion. */
    Fixed=Fixed>>(MAD_F_FRACBITS-15);
    return((signed short)Fixed);
}

/****************************************************************************
 * Print human readable informations about an audio MPEG frame.                *
 ****************************************************************************/
static int PrintFrameInfo(FILE *fp, struct mad_header *Header)
{
    const char    *Layer,
                *Mode,
                *Emphasis;

    /* Convert the layer number to it's printed representation. */
    switch(Header->layer)
    {
        case MAD_LAYER_I:
            Layer="I";
            break;
        case MAD_LAYER_II:
            Layer="II";
            break;
        case MAD_LAYER_III:
            Layer="III";
            break;
        default:
            Layer="(unexpected layer value)";
            break;
    }

    /* Convert the audio mode to it's printed representation. */
    switch(Header->mode)
    {
        case MAD_MODE_SINGLE_CHANNEL:
            Mode="single channel";
            break;
        case MAD_MODE_DUAL_CHANNEL:
            Mode="dual channel";
            break;
        case MAD_MODE_JOINT_STEREO:
            Mode="joint (MS/intensity) stereo";
            break;
        case MAD_MODE_STEREO:
            Mode="normal LR stereo";
            break;
        default:
            Mode="(unexpected mode value)";
            break;
    }

    /* Convert the emphasis to it's printed representation. Note that
     * the MAD_EMPHASIS_RESERVED enumeration value appread in libmad
     * version 0.15.0b.
     */
    switch(Header->emphasis)
    {
        case MAD_EMPHASIS_NONE:
            Emphasis="no";
            break;
        case MAD_EMPHASIS_50_15_US:
            Emphasis="50/15 us";
            break;
        case MAD_EMPHASIS_CCITT_J_17:
            Emphasis="CCITT J.17";
            break;
#if (MAD_VERSION_MAJOR>=1) || \
    ((MAD_VERSION_MAJOR==0) && (MAD_VERSION_MINOR>=15))
        case MAD_EMPHASIS_RESERVED:
            Emphasis="reserved(!)";
            break;
#endif
        default:
            Emphasis="(unexpected emphasis value)";
            break;
    }

    fprintf(fp,"%s: %lu kb/s audio mpeg layer %s stream %s crc, "
            "%s with %s emphasis at %d Hz sample rate\n",
            ProgName,Header->bitrate,Layer,
            Header->flags&MAD_FLAG_PROTECTION?"with":"without",
            Mode,Emphasis,Header->samplerate);
    return(ferror(fp));
}

/****************************************************************************
 * Main decoding loop. This is where mad is used.                            *
 ****************************************************************************/
#define INPUT_BUFFER_SIZE    (5*8192)
#define OUTPUT_BUFFER_SIZE    8192 /* Must be an integer multiple of 4. */
static int MpegAudioDecoder(FILE *InputFp)
{
    struct mad_stream    Stream;
    struct mad_frame    Frame;
    struct mad_synth    Synth;
    mad_timer_t            Timer;
    unsigned char        InputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD],
                        OutputBuffer[OUTPUT_BUFFER_SIZE],
                        *OutputPtr=OutputBuffer;
    const unsigned char    *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE;
    int                    Status=0,
                        i;
    unsigned long        FrameCount=0;
    bstdfile_t            *BstdFile;

    /* First the structures used by libmad must be initialized. */
    mad_stream_init(&Stream);
    mad_frame_init(&Frame);
    mad_synth_init(&Synth);
    mad_timer_reset(&Timer);

    /* Decoding options can here be set in the options field of the
     * Stream structure.
     */

    /* {1} When decoding from a file we need to know when the end of
     * the file is reached at the same time as the last bytes are read
     * (see also the comment marked {3} bellow). Neither the standard
     * C fread() function nor the posix read() system call provides
     * this feature. We thus need to perform our reads through an
     * interface having this feature, this is implemented here by the
     * bstdfile.c module.
     */
    BstdFile=NewBstdFile(InputFp);
    if(BstdFile==NULL)
    {
        fprintf(stderr,"%s: can't create a new bstdfile_t (%s).\n",
                ProgName,strerror(errno));
        return(1);
    }

    /* This is the decoding loop. */
    do
    {
        /* The input bucket must be filled if it becomes empty or if
         * it's the first execution of the loop.
         */
        if(Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN)
        {
            size_t            ReadSize,
                            Remaining;
            unsigned char    *ReadStart;

            /* {2} libmad may not consume all bytes of the input
             * buffer. If the last frame in the buffer is not wholly
             * contained by it, then that frame's start is pointed by
             * the next_frame member of the Stream structure. This
             * common situation occurs when mad_frame_decode() fails,
             * sets the stream error code to MAD_ERROR_BUFLEN, and
             * sets the next_frame pointer to a non NULL value. (See
             * also the comment marked {4} bellow.)
             *
             * When this occurs, the remaining unused bytes must be
             * put back at the beginning of the buffer and taken in
             * account before refilling the buffer. This means that
             * the input buffer must be large enough to hold a whole
             * frame at the highest observable bit-rate (currently 448
             * kb/s). XXX=XXX Is 2016 bytes the size of the largest
             * frame? (448000*(1152/32000))/8
             */
            if(Stream.next_frame!=NULL)
            {
                Remaining=Stream.bufend-Stream.next_frame;
                memmove(InputBuffer,Stream.next_frame,Remaining);
                ReadStart=InputBuffer+Remaining;
                ReadSize=INPUT_BUFFER_SIZE-Remaining;
            }
            else
                ReadSize=INPUT_BUFFER_SIZE,
                    ReadStart=InputBuffer,
                    Remaining=0;
           
            /* Fill-in the buffer. If an error occurs print a message
             * and leave the decoding loop. If the end of stream is
             * reached we also leave the loop but the return status is
             * left untouched.
             */
            ReadSize=BstdRead(ReadStart,1,ReadSize,BstdFile);
            if(ReadSize<=0)
            {
                if(ferror(InputFp))
                {
                    fprintf(stderr,"%s: read error on bitstream (%s)\n",
                            ProgName,strerror(errno));
                    Status=1;
                }
                if(feof(InputFp))
                    fprintf(stderr,"%s: end of input stream\n",ProgName);
                break;
            }

            /* {3} When decoding the last frame of a file, it must be
             * followed by MAD_BUFFER_GUARD zero bytes if one wants to
             * decode that last frame. When the end of file is
             * detected we append that quantity of bytes at the end of
             * the available data. Note that the buffer can't overflow
             * as the guard size was allocated but not used the the
             * buffer managment code. (See also the comment marked
             * {1}.)
             *
             * In a message to the mad-dev mailing list on May 29th,
             * 2001, Rob leslie explains the guard zone as follows:
             *
             *    "The reason for MAD_BUFFER_GUARD has to do with the
             *    way decoding is performed. In Layer III, Huffman
             *    decoding may inadvertently read a few bytes beyond
             *    the end of the buffer in the case of certain invalid
             *    input. This is not detected until after the fact. To
             *    prevent this from causing problems, and also to
             *    ensure the next frame's main_data_begin pointer is
             *    always accessible, MAD requires MAD_BUFFER_GUARD
             *    (currently 8) bytes to be present in the buffer past
             *    the end of the current frame in order to decode the
             *    frame."
             */
            if(BstdFileEofP(BstdFile))
            {
                memset(ReadStart+ReadSize,0,MAD_BUFFER_GUARD);
                ReadSize+=MAD_BUFFER_GUARD;
            }

            /* Pipe the new buffer content to libmad's stream decoder
             * facility.
             */
            mad_stream_buffer(&Stream,InputBuffer,ReadSize+Remaining);
            Stream.error=0;
        }

        /* Decode the next mpeg frame. The streams is read from the
         * buffer, its constituents are break down and stored the the
         * Frame structure, ready for examination/alteration or PCM
         * synthesis. Decoding options are carried in the Frame
         * structure from the Stream structure.
         *
         * Error handling: mad_frame_decode() returns a non zero value
         * when an error occurs. The error condition can be checked in
         * the error member of the Stream structure. A mad error is
         * recoverable or fatal, the error status is checked with the
         * MAD_RECOVERABLE macro.
         *
         * {4} When a fatal error is encountered all decoding
         * activities shall be stopped, except when a MAD_ERROR_BUFLEN
         * is signaled. This condition means that the
         * mad_frame_decode() function needs more input to achieve
         * it's work. One shall refill the buffer and repeat the
         * mad_frame_decode() call. Some bytes may be left unused at
         * the end of the buffer if those bytes forms an incomplete
         * frame. Before refilling, the remainign bytes must be moved
         * to the begining of the buffer and used for input for the
         * next mad_frame_decode() invocation. (See the comments marked
         * {2} earlier for more details.)
         *
         * Recoverable errors are caused by malformed bit-streams, in
         * this case one can call again mad_frame_decode() in order to
         * skip the faulty part and re-sync to the next frame.
         */
        if(mad_frame_decode(&Frame,&Stream))
        {
            if(MAD_RECOVERABLE(Stream.error))
            {
                fprintf(stderr,"%s: recoverable frame level error (%s)\n",
                        ProgName,MadErrorString(&Stream));
                fflush(stderr);
                continue;
            }
            else
                if(Stream.error==MAD_ERROR_BUFLEN)
                    continue;
                else
                {
                    fprintf(stderr,"%s: unrecoverable frame level error (%s).\n",
                            ProgName,MadErrorString(&Stream));
                    Status=1;
                    break;
                }
        }

        /* The characteristics of the stream's first frame is printed
         * on stderr. The first frame is representative of the entire
         * stream.
         */
        if(FrameCount==0)
            if(PrintFrameInfo(stderr,&Frame.header))
            {
                Status=1;
                break;
            }

        /* Accounting. The computed frame duration is in the frame
         * header structure. It is expressed as a fixed point number
         * whole data type is mad_timer_t. It is different from the
         * samples fixed point format and unlike it, it can't directly
         * be added or substracted. The timer module provides several
         * functions to operate on such numbers. Be careful there, as
         * some functions of mad's timer module receive some of their
         * mad_timer_t arguments by value!
         */
        FrameCount++;
        mad_timer_add(&Timer,Frame.header.duration);

        /* Between the frame decoding and samples synthesis we can
         * perform some operations on the audio data. We do this only
         * if some processing was required. Detailed explanations are
         * given in the ApplyFilter() function.
         */
        //if(DoFilter)
        //    ApplyFilter(&Frame);
               
        /* Once decoded the frame is synthesized to PCM samples. No errors
         * are reported by mad_synth_frame();
         */
        mad_synth_frame(&Synth,&Frame);

        /* Synthesized samples must be converted from mad's fixed
         * point number to the consumer format. Here we use unsigned
         * 16 bit big endian integers on two channels. Integer samples
         * are temporarily stored in a buffer that is flushed when
         * full.
         */
        for(i=0;i<Synth.pcm.length;i++)
        {
            signed short    Sample;

            /* Left channel */
            Sample=MadFixedToSshort(Synth.pcm.samples[0]);
            *(OutputPtr++)=Sample>>0;
            *(OutputPtr++)=Sample>>8;

            /* Right channel. If the decoded stream is monophonic then
             * the right output channel is the same as the left one.
             */
            if(MAD_NCHANNELS(&Frame.header)==2)
                Sample=MadFixedToSshort(Synth.pcm.samples[1]);
            *(OutputPtr++)=Sample>>0;
            *(OutputPtr++)=Sample>>8;

            /* Flush the output buffer if it is full. */
            if(OutputPtr==OutputBufferEnd)
            {
                //if(fwrite(OutputBuffer,1,OUTPUT_BUFFER_SIZE,OutputFp)!=OUTPUT_BUFFER_SIZE)
                if(write(sfd, OutputBuffer, OUTPUT_BUFFER_SIZE)!=OUTPUT_BUFFER_SIZE)
                {
                    fprintf(stderr,"%s: PCM write error (%s).\n",
                            ProgName,strerror(errno));
                    Status=2;
                    break;
                }
                OutputPtr=OutputBuffer;
            }
        }
    }while(1);

    /* The input file was completely read; the memory allocated by our
     * reading module must be reclaimed.
     */
    BstdFileDestroy(BstdFile);

    /* Mad is no longer used, the structures that were initialized must
     * now be cleared.
     */
    mad_synth_finish(&Synth);
    mad_frame_finish(&Frame);
    mad_stream_finish(&Stream);

    /* If the output buffer is not empty and no error occured during
     * the last write, then flush it.
     */
    if(OutputPtr!=OutputBuffer && Status!=2)
    {
        size_t    BufferSize=OutputPtr-OutputBuffer;

        //if(fwrite(OutputBuffer,1,BufferSize,OutputFp)!=BufferSize)
        if(write(sfd,OutputBuffer,BufferSize)!=BufferSize)
        {
            fprintf(stderr,"%s: PCM write error (%s).\n",
                    ProgName,strerror(errno));
            Status=2;
        }
    }

    /* Accounting report if no error occured. */
    if(!Status)
    {
        char    Buffer[80];

        /* The duration timer is converted to a human readable string
         * with the versatile, but still constrained mad_timer_string()
         * function, in a fashion not unlike strftime(). The main
         * difference is that the timer is broken into several
         * values according some of it's arguments. The units and
         * fracunits arguments specify the intended conversion to be
         * executed.
         *
         * The conversion unit (MAD_UNIT_MINUTES in our example) also
         * specify the order and kind of conversion specifications
         * that can be used in the format string.
         *
         * It is best to examine mad's timer.c source-code for details
         * of the available units, fraction of units, their meanings,
         * the format arguments, etc.
         */
        mad_timer_string(Timer,Buffer,"%lu:%02lu.%03u",
                         MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0);
        fprintf(stderr,"%s: %lu frames decoded (%s).\n",
                ProgName,FrameCount,Buffer);
    }

    /* That's the end of the world (in the H. G. Wells way). */
    return(Status);
}

/****************************************************************************
 * Program entry point.                                                        *
 ****************************************************************************/
int main(int argc, char *argv[])
{

    int Status;
    int i=0;   

    FILE *file_fd;    //文件
long file_len;    //文件长度
long loops;


    unsigned char audio_buffer[BUF_SIZE];
    unsigned char *file_name;

    printf("This is a wav&mp3 play program.\n");
    printf("Please add a file name and the audio rate!\nsuch as \"./test_music test.mp3\" \n\n\n");
    //delay(100000);   

    ProgName=argv[0];       
    file_name = argv[1];
    file_len = strlen(file_name);
   
    if(file_len < 3)
    {
        printf("file is too short!\n");
        exit(0);
    }           
   
    printf("File name : %s \n",file_name);
    //delay(100000);
//if file format is mp3//
if(file_name[file_len-3]=='m' && file_name[file_len-2]=='p' && file_name[file_len-1]=='3')
{
        printf("Play mp3!\n");

//        fd = open(file_name, O_RDONLY);
            file_fd = fopen(file_name, "r");

        if ((sfd = open("/dev/iis", O_WRONLY)) < 0)
    {
        printf("can not open device!!!\n");
        return 5;
        }

    /* Decode stdin to stdout. */
    Status=MpegAudioDecoder(file_fd);
    if(Status)
        fprintf(stderr,"%s: an error occured during decoding.\n",ProgName);

    /* All done. */

    fclose(file_fd);
    close(sfd);
    return(Status);
}
else
{
    /*打开音频设备,准备play*/   
    if ((sfd = open(DEVICE_NAME, O_WRONLY)) == -1)
    {
        printf("open error\n");   
        return -1;
    }

    /*打开并计算文件长度*/
    file_fd = fopen(file_name, "r");
    fseek(file_fd,0,SEEK_END);     //定位到文件末 
    file_len = ftell(file_fd);     //文件长度

    loops = file_len/(BUF_SIZE);
   
    /*重新定位到文件头*/
    fclose(file_fd);
    file_fd = fopen(file_name, "r");
    /*播放wav文件*/
    for(i=0;i<loops;i++)
    {
        fread(audio_buffer, BUF_SIZE, 1, file_fd);
        //printf("fread_ret=%d,loops=%d,i=%d\n",fread_ret,loops,i);   
        write(sfd,audio_buffer,BUF_SIZE);
    }
    /*关闭设备和文件*/
    fclose(file_fd);
    close(sfd);
}
    return 0;

}
/*
 * Local Variables:
 * tab-width: 4
 * End:
 */

/****************************************************************************
 * End of file madllc.c                                                        *
 ****************************************************************************/
好,准备好这些文件后,开始动手,编译连接libmad,生成应用程序madlld。
 arm-elf-gcc -static -Wl,-elf2flt -o madlld madlld.c  bstdfile.c ./libmad.a
   同样放到根文件系统bin下面,开始测试:
点击看大图


哈哈,总算成功拉!
这是源码code大礼包!
https://static.assets-stash.eet-china.com/album/old-resources/2010/4/24/f2c44bca-d69a-4e25-9a8b-2dc226c37f06.zip

来张照片合影下
点击看大图
这是手稿
点击看大图

文章评论0条评论)

登录后参与讨论
我要评论
0
8
关闭 站长推荐上一条 /2 下一条