嵌入式alsa+libmad實現mp3播放

<pre name="code" class="cpp">/*
 * libmad - MPEG audio decoder library
 * Copyright (C) 2000-2004 Underbit Technologies, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: minimad.c,v 1.4 2004/01/23 09:41:32 rob Exp $
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include "alsa/asoundlib.h"
#include "mad.h"
#include "player_ipc_interface.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.
 */

#define DEBUG(x,y...)	//{printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}
#define ERROR(x,y...)	{printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}

typedef struct mad_decoder_s {
  struct mad_synth  synth;
  struct mad_stream stream;
  struct mad_frame  frame;
} mad_decoder_t;

extern player_shm_t *shm_buff;
static int to_stop_play = 0;

static inline void play_frame(mad_decoder_t *mad_decoder);
static inline signed int scale(mad_fixed_t sample);
static int set_pcm();
static snd_pcm_t *handle = NULL;        //PCI設備句柄
static snd_pcm_hw_params_t* params = NULL;//硬件信息和PCM流配置
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int libmad_play_mp3(char *mp3_file, res_type_t type)
{
	struct stat stat;
	int fd;
	int count;
	char *buff = NULL;
	mad_decoder_t *this;
	int buf_size = 1152;
	int buf_len = 0;

	this = calloc(1, sizeof(mad_decoder_t));
	buff = malloc(buf_size);
	if(buff)
		memset(buff,0,buf_size);

	mad_synth_init  (&this->synth);
	mad_stream_init (&this->stream);
	mad_frame_init  (&this->frame);

	if(NULL == mp3_file || strlen(mp3_file) == 0){
		ERROR("mp3_file is NULL of empty.");
		return -1;
	}

	if(shm_buff){
		strcpy(shm_buff->url, mp3_file);
		shm_buff->type = type;
		shm_buff->is_playing = 1;
	}else{
		ERROR("player share memory shm_buff is NULL\n");
		return -2;
	}

	fd = open(mp3_file, O_RDONLY);
	if(fd < 0){
		perror("open file failed:");
		return -2;
	}    

	if (fstat(fd, &stat) == -1 || stat.st_size == 0){
		ERROR("fstat failed:\n");
		return -3;
	}

	if(set_pcm(fd, this)!=0){                 //設置pcm 參數
		ERROR("set_pcm fialed:\n");
		return -4;   
	}

	while((count = read(fd, buff+buf_len, buf_size-buf_len)) > 0){
		buf_len += count;
		if(count < 0){
			ERROR("read mp3_file error:%s", strerror(errno));
			return -5;
		}
		if(to_stop_play){
			break;
		}
		while(1){
			int ret;
			mad_stream_buffer (&this->stream, (unsigned char *)buff, buf_len);
			ret=mad_frame_decode (&this->frame, &this->stream);
			if (this->stream.next_frame) {
				int num_bytes =
					(char*)buff+buf_len - (char*)this->stream.next_frame;
				memmove(buff, this->stream.next_frame, num_bytes);
				buf_len = num_bytes;
			}
			if (ret == 0)
				play_frame(this);
			// error! try to resync!
			if(this->stream.error==MAD_ERROR_BUFLEN) break;
		}
	}

	if(shm_buff){
		memset(shm_buff->url, 0, URL_SIZE);
		shm_buff->type = UNKNOWN_TYPE;
		shm_buff->is_playing = 0;
		pthread_mutex_lock(&mutex);
		to_stop_play = 0;
		pthread_mutex_unlock(&mutex);
	}
	mad_synth_finish (&this->synth);
	mad_frame_finish (&this->frame);
	mad_stream_finish(&this->stream);
	free(this);
	free(buff);
	close(fd);
	snd_pcm_drain(handle);
	snd_pcm_close(handle);
	return 0;
}

static inline void play_frame(mad_decoder_t *this)
{
	mad_synth_frame (&this->synth, &this->frame);

	unsigned int nchannels, nsamples,n;
	mad_fixed_t const *left_ch, *right_ch;
	struct mad_pcm      *pcm = &this->synth.pcm;

	/* pcm->samplerate contains the sampling frequency */

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

	unsigned char Output[6912], *OutputPtr;  
	//int fmt, wrote, speed, exact_rate, err, dir; 
	//   printf("This is output\n");
	OutputPtr = Output;  
	while (nsamples--) 
	{
		signed int sample;
		/* output sample(s) in 16-bit signed little-endian PCM */
		sample = scale(*left_ch++);
		*(OutputPtr++) = sample >> 0;  
		*(OutputPtr++) = sample >> 8;  
		if (nchannels == 2)  
		{  
			sample = scale (*right_ch++);  
			*(OutputPtr++) = sample >> 0;  
			*(OutputPtr++) = sample >> 8;  
		}  
	}
	snd_pcm_writei (handle, Output, n);  
}

void libmad_stop_play_mp3()
{
	DEBUG("url:%s, type:%d, is_playing:%d\n", shm_buff->url, shm_buff->type, shm_buff->is_playing);
	if(shm_buff && shm_buff->type == CAN_STOP && shm_buff->is_playing){
		memset(shm_buff->url, 0, URL_SIZE);
		shm_buff->type = UNKNOWN_TYPE;
		shm_buff->is_playing = 0;
		pthread_mutex_lock(&mutex);
		to_stop_play = 1;
		pthread_mutex_unlock(&mutex);
	}
}

static int set_pcm(int fd, mad_decoder_t *this)
{
	int rc;     
	int dir=0;
	unsigned int rate = 44100;;                /* 採樣頻率 44.1KHz*/
	//int format = SND_PCM_FORMAT_S16_LE; /*     量化位數 16      */
	int channels = 2;                 /*     聲道數 2           */

	char *buff = NULL;
	int buf_size = 1152;
	int buf_len = 0;
	int count;
	int flag = 0;
	buff = malloc(buf_size);
	if(buff)
		memset(buff,0,buf_size);

	while((count = read(fd, buff+buf_len, buf_size-buf_len)) > 0){
		buf_len += count;
		if(count < 0){
			ERROR("read mp3_file error:%s", strerror(errno));
			return -5;
		}
		while(1){
			int ret;
			mad_stream_buffer (&this->stream, (unsigned char *)buff, buf_len);
			ret=mad_frame_decode (&this->frame, &this->stream);
			if (this->stream.next_frame) {
				int num_bytes =
					(char*)buff+buf_len - (char*)this->stream.next_frame;
				memmove(buff, this->stream.next_frame, num_bytes);
				buf_len = num_bytes;
			}
			if (ret == 0){
				channels = MAD_NCHANNELS(&(this->frame.header)); //動態設置通道數,採樣率
				rate = this->frame.header.samplerate;
				flag = 1;
				break;
			}
		}
		if(flag)
			break;
	}
	lseek(fd, 0, SEEK_SET);
	free(buff);

	rc=snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
	if(rc<0){
		perror("\nopen PCM device failed:");
		exit(1);
	}
	snd_pcm_hw_params_alloca(¶ms); //分配params結構體

	rc=snd_pcm_hw_params_any(handle, params);//初始化params
	if(rc<0){
		perror("\nsnd_pcm_hw_params_any:");
		exit(1);
	}
	rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);                                 //初始化訪問權限
	if(rc<0){
		perror("\nsed_pcm_hw_set_access:");
		exit(1);

	}

	rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);             //設置16位採樣精度  
	if(rc<0){
		perror("snd_pcm_hw_params_set_format failed:");
		exit(1);
	} 

	rc=snd_pcm_hw_params_set_channels(handle, params, channels);  //設置聲道,1表示單聲>道,2表示立體聲
	if(rc<0){
		perror("\nsnd_pcm_hw_params_set_channels:");
		exit(1);
	}    

	rc=snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);  //設置>頻率
	if(rc<0){
		perror("\nsnd_pcm_hw_params_set_rate_near:");
		exit(1);
	}   

	rc = snd_pcm_hw_params(handle, params);
	if(rc<0){
		perror("\nsnd_pcm_hw_params: ");
		exit(1);
	} 

	return 0;              
}

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章