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

来源:本站
导读:目前正在解读《采用libmad+libao实现最简单的mp3播放器》的相关信息,《采用libmad+libao实现最简单的mp3播放器》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《采用libmad+libao实现最简单的mp3播放器》的详细说明。
简介:本文章是关于采用libmad+libao实现最简单的mp3播放器。

一、概述

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 %un",

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;

}

提醒:《采用libmad+libao实现最简单的mp3播放器》最后刷新时间 2024-03-14 01:12:53,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《采用libmad+libao实现最简单的mp3播放器》该内容的真实性请自行鉴别。