注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题

swz的个人空间 http://home.eeworld.com.cn/space-uid-179477.html [收藏] [复制] [分享] [RSS]

日志

采用libmad+libao实现最简单的mp3播放器

已有 63693 次阅读2010-5-19 15:26 |个人分类:嵌入式|

一、概述

1.libmad介绍


MAD (libmad) is a high-quality MPEG audio decoder. It currently supports
MPEG-1 and the MPEG-2 extension to Lower Sampling Frequencies, as well as
the so-called MPEG 2.5 format. All three audio layers (Layer I, Layer II,
and Layer III a.k.a. MP3) are fully implemented.

MAD does not yet support MPEG-2 multichannel audio (although it should be
backward compatible with such streams) nor does it currently support AAC.

MAD has the following special features:

- 24-bit PCM output
- 100% fixed-point (integer) computation
- completely new implementation based on the ISO/IEC standards
- distributed under the terms of the GNU General Public License (GPL)

2.libao介绍
libao is a cross platform audio library that allows program to output audio using a simple API on a wide varity of platform. It currently supports:
   . Null output (handy for testing without a sound device)
   . Wav files
   .
AV files
   . OSS (open sound system, used on linux and freebsd)
   . ALSA (advanced linux sound archiecture)
   .
PulseAudio (next generation GNOME sound server )
   . esd (EsounD or enlightened sound daemon)
   .
AIX
   . Sun/ NetBSD/OpenBSD
   . IRIX
   . NAS (network audio server)

二、源代码说明

libmad附带了一个示例程序minimad, 但是仅仅是将解码结果输出到屏幕上,而libao则是基于OSS、ALSA等之上的音频高级API,可以将pcm输出,通过多种方式播放出来,因此将两者结合起来,编写一个可以播放mp3的程序。


编译用的脚本

#!/bin/bash
gcc -o minimad minimad.c -I . -lmad -lao -lm
~

改进后的源代码清单

# include <stdio.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/mman.h>

# include "mad.h"
#i nclude <ao/ao.h>
#i nclude <math.h>


/*
* This is perhaps the simplest example use of the MAD high-level API.
* Standard input is mapped into memory via mmap(), then the high-level API
* is invoked with three callbacks: input, output, and error. The output
* callback converts MAD's high-resolution PCM samples to 16 bits, then
* writes them to standard output in little-endian, stereo-interleaved
* format.
*/

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

ao_device *device;
ao_sample_format format;
int default_driver;


int main(int argc, char *argv[])
{

struct stat stat;
void *fdm;
//gn add begin
ao_initialize();

// -- Setup for default driver --

default_driver = ao_default_driver_id();

format.bits = 16;
format.channels = 2;
format.rate = 44100;
format.byte_format = AO_FMT_LITTLE;

// -- Open driver --
device = ao_open_live(default_driver, &format, NULL );
if (device == NULL) {
fprintf(stderr, "Error opening device.\n");
exit (1);
}

//gn add end

if (argc != 1)
return 1;

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

fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, STDIN_FILENO, 0);
if (fdm == MAP_FAILED)
return 3;

decode(fdm, stat.st_size);

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

//gnadd begin
//myplay();
ao_close(device);
ao_shutdown();

//gnadd end

return 0;
}

/*
* This is a private message structure. A generic pointer to this structure
* is passed to each of the callback functions. Put here any data you need
* to access from within the callbacks.
*/

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

/*
* This is the input callback. The purpose of this callback is to (re)fill
* the stream buffer which is to be decoded. In this example, an entire file
* has been mapped into memory, so we just call mad_stream_buffer() with the
* address and length of the mapping. When this callback is called a second
* time, we are finished decoding.
*/

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

/*
* The following utility routine performs simple rounding, clipping, and
* scaling of MAD's high-resolution samples down to 16 bits. It does not
* perform any dithering or noise shaping, which would be recommended to
* obtain any exceptional audio quality. It is therefore not recommended to
* use this routine if high-quality output is desired.
*/

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

/*
* This is the output callback function. It is called after each frame of
* MPEG audio data has been completely decoded. The purpose of this callback
* is to output (or play) the decoded PCM audio.
*/

static void myplay()
{
char *buffer;
int buf_size;
int sample;
float freq = 440.0;
int i;

buf_size = format.bits/8 * format.channels * format.rate;
buffer = calloc(buf_size,
sizeof(char));

for (i = 0; i < format.rate; i++) {
sample = (int)(0.75 * 32768.0 *
sin(2 * M_PI * freq * ((float) i/format.rate)));

/* Put the same stuff in left and right channel */
buffer[4*i] = buffer[4*i+2] = sample & 0xff;
buffer[4*i+1] = buffer[4*i+3] = (sample >> 8) & 0xff;
}
ao_play(device, buffer, buf_size);

}

struct audio_dither {
mad_fixed_t error[3];
mad_fixed_t random;
};

/*
* NAME: prng()
* DESCRIPTION: 32-bit pseudo-random number generator
*/
static inline
unsigned long prng(unsigned long state)
{
return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
}

/*
* NAME: audio_linear_dither()
* DESCRIPTION: generic linear sample quantize and dither routine
*/
inline
signed long audio_linear_dither(unsigned int bits, mad_fixed_t sample,
struct audio_dither *dither)
{
unsigned int scalebits;
mad_fixed_t output, mask, random;

enum {
MIN = -MAD_F_ONE,
MAX = MAD_F_ONE - 1
};

/* noise shape */
sample += dither->error[0] - dither->error[1] + dither->error[2];

dither->error[2] = dither->error[1];
dither->error[1] = dither->error[0] / 2;

/* bias */
output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1));

scalebits = MAD_F_FRACBITS + 1 - bits;
mask = (1L << scalebits) - 1;

/* dither */
random = prng(dither->random);
output += (random & mask) - (dither->random & mask);

dither->random = random;

/* clip */
if (output > MAX) {
output = MAX;

if (sample > MAX)
sample = MAX;
}
else if (output < MIN) {
output = MIN;

if (sample < MIN)
sample = MIN;
}

/* quantize */
output &= ~mask;

/* error feedback */
dither->error[0] = sample - output;

/* scale */
return output >> scalebits;
}


enum mad_flow output(void *data,
struct mad_header const *header,
struct mad_pcm *pcm)
{
register int nsamples = pcm->length;
mad_fixed_t const *left_ch = pcm->samples[0], *right_ch = pcm->samples[1];

static unsigned char stream[1152*4]; /* 1152 because that's what mad has as a max; *4 because
there are 4 distinct bytes per sample (in 2 channel case) */
static unsigned int rate = 0;
static int channels = 0;
static struct audio_dither dither;

register char * ptr = stream;
register signed int sample;
register mad_fixed_t tempsample;

/* We need to know information about the file before we can open the playdevice
in some cases. So, we do it here. */

if (pcm->channels == 2)
{
while (nsamples--)
{
signed int sample;
sample = scale(*left_ch++);
// sample = (signed int) audio_linear_dither(16, tempsample, &dither);
stream[(pcm->length-nsamples)*4 ] = ((sample >> 0) & 0xff);
stream[(pcm->length-nsamples)*4 +1] = ((sample >> 8) & 0xff);

sample = scale(*right_ch++);
stream[(pcm->length-nsamples)*4+2 ] = ((sample >> 0) & 0xff);
stream[(pcm->length-nsamples)*4 +3] = ((sample >> 8) & 0xff);
}
ao_play(device, stream, pcm->length * 4);
}
else /* Just straight mono output */
{
while (nsamples--)
{
signed int sample;
sample = scale(*left_ch++);
stream[(pcm->length-nsamples)*2 ] = ((sample >> 0) & 0xff);
stream[(pcm->length-nsamples)*2 +1] = ((sample >> 8) & 0xff);
}
ao_play(device, stream, pcm->length * 2);
}

return MAD_FLOW_CONTINUE;
}

/*
* This is the error callback function. It is called whenever a decoding
* error occurs. The error is indicated by stream->error; the list of
* possible MAD_ERROR_* errors can be found in the mad.h (or
* libmad/stream.h) header file.
*/

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_BREAK here to stop decoding (and propagate an error) */

return MAD_FLOW_CONTINUE;
}

/*
* This is the function called by main() above to perform all the
* decoding. It instantiates a decoder object and configures it with the
* input, output, and error callback functions above. A single call to
* mad_decoder_run() continues until a callback function returns
* MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
* signal an error).
*/

static
int decode(unsigned char const *start, unsigned long length)
{
struct buffer buffer;
struct mad_decoder decoder;
int result;

/* initialize our private message structure */

buffer.start = start;
buffer.length = length;

/* configure input, output, and error functions */

mad_decoder_init(&decoder, &buffer,
input, 0 /* header */, 0 /* filter */, output,
error, 0 /* message */);

/* start decoding */

result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

/* release the decoder */

mad_decoder_finish(&decoder);

return result;
}

发表评论 评论 (1 个评论)

回复 小志 2010-5-20 11:24
呵呵,多谢分享:)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

小黑屋|手机版|Archiver|电子工程世界 ( 京ICP证 060456

GMT+8, 2018-8-19 03:50 , Processed in 0.017502 second(s), 12 queries , Gzip On, Redis On.

Powered by EEWORLD电子工程世界

© 2018 http://bbs.eeworld.com.cn/

返回顶部