嵌入式alsa+libmad实现mp3播放

2019-07-13 05:12发布

/*
 * 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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("
");}
#define ERROR(x,y...)	{printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("
");}

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
");
		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:
");
		return -3;
	}

	if(set_pcm(fd, this)!=0){                 //设置pcm 参数
		ERROR("set_pcm fialed:
");
		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
");
	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
", 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("
open 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("
snd_pcm_hw_params_any:");
		exit(1);
	}
	rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);                                 //初始化访问权限
	if(rc<0){
		perror("
sed_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("
snd_pcm_hw_params_set_channels:");
		exit(1);
	}    

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

	rc = snd_pcm_hw_params(handle, params);
	if(rc<0){
		perror("
snd_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);
}