/********************************************************************************************
* author:conowen@大钟
* E-mail:[email protected]
*site:http://www.idealpwr.com/
*深圳市动力思维科技发展有限公司
* http://blog.csdn.net/conowen
* 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。
********************************************************************************************/
众所周知,Android的audiotrack只能播放原始的音频,也就是PCM数据,若是播放mp3编码格式的音频的话,就是出现沙沙的噪音。所以,可以使用第三方库Libmad来对mp3文件解码称为PCM数据,再送给audiotrack播放即可。
1、Libmad简介
Libmad是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和 API 等。
2、实现过程
2.1、下载Android平台下的Libmad工程
http://gitorious.org/rowboat/external-libmad
或者直接执行
mkdir libmad cd libmad git clone git://gitorious.org/rowboat/external-libmad.git
下载的project可以直接用NDK编译通过的,但是要使用还是要写jni层供java层调用,关于NDK开发,可以参看我之前的博文。
但是Android.mk要改成如下形式。
#ifeq ($(strip $(BUILD_WITH_GST)),true) LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ version.c \ fixed.c \ bit.c \ timer.c \ stream.c \ frame.c \ synth.c \ decoder.c \ layer12.c \ layer3.c \ huffman.c LOCAL_ARM_MODE := arm LOCAL_MODULE:= libmad LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/android LOCAL_CFLAGS := -DHAVE_CONFIG_H -DFPM_ARM -ffast-math -O3 include $(BUILD_SHARED_LIBRARY) #endif
2.2、C代码编写API
需要注意一点的是,得到音频的Samplerate(采样率)要先进行一次readSamples操作才能发采样率读出。具体看代码即可。
#define TAG "NativeMP3Decoder" #include "FileSystem.h" #include "Mad.h" #include "NativeMP3Decoder.h" #include <stdio.h> #include <string.h> #include <stdlib.h> #include <android/log.h> #define SHRT_MAX (32767) #define INPUT_BUFFER_SIZE (8192/4) #define OUTPUT_BUFFER_SIZE 8192 /* Must be an integer multiple of 4. */ //int g_size; extern int file_open(const char *filename, int flags); extern int file_read(T_pFILE fd, unsigned char *buf, int size); extern int file_write(T_pFILE fd, unsigned char *buf, int size); extern int64_t file_seek(T_pFILE fd, int64_t pos, int whence); extern int file_close(T_pFILE fd); /** * Struct holding the pointer to a wave file. */ typedef struct { int size; int64_t fileStartPos; T_pFILE file; struct mad_stream stream; struct mad_frame frame; struct mad_synth synth; mad_timer_t timer; int leftSamples; int offset; unsigned char inputBuffer[INPUT_BUFFER_SIZE]; } MP3FileHandle; /** static WaveFileHandle array **/ static inline int readNextFrame( MP3FileHandle* mp3 ); static MP3FileHandle* Handle; unsigned int g_Samplerate; /** * Seeks a free handle in the handles array and returns its index or -1 if no handle could be found */ static inline void closeHandle() { file_close( Handle->file); mad_synth_finish(&Handle->synth); mad_frame_finish(&Handle->frame); mad_stream_finish(&Handle->stream); free(Handle); Handle = NULL; } static inline signed short fixedToShort(mad_fixed_t Fixed) { if(Fixed>=MAD_F_ONE) return(SHRT_MAX); if(Fixed<=-MAD_F_ONE) return(-SHRT_MAX); Fixed=Fixed>>(MAD_F_FRACBITS-15); return((signed short)Fixed); } int NativeMP3Decoder_init(char * filepath,unsigned long start/*,unsigned long size*/) { __android_log_print(ANDROID_LOG_INFO, TAG, "start = %d*******\n",start); // __android_log_print(ANDROID_LOG_INFO, TAG, "size = %d*******\n",size); //g_size=size; T_pFILE fileHandle = file_open( filepath, _FMODE_READ); if( fileHandle == 0 ) return -1; MP3FileHandle* mp3Handle = (MP3FileHandle*)malloc(sizeof(MP3FileHandle)); memset(mp3Handle, 0, sizeof(MP3FileHandle)); mp3Handle->file = fileHandle; // mp3Handle->size = size; mp3Handle->fileStartPos= start; file_seek( mp3Handle->file, start, SEEK_SET); mad_stream_init(&mp3Handle->stream); mad_frame_init(&mp3Handle->frame); mad_synth_init(&mp3Handle->synth); mad_timer_reset(&mp3Handle->timer); Handle = mp3Handle; readNextFrame( Handle ); g_Samplerate = Handle->frame.header.samplerate; return 1; } static inline int readNextFrame( MP3FileHandle* mp3 ) { do { if( mp3->stream.buffer == 0 || mp3->stream.error == MAD_ERROR_BUFLEN ) { int inputBufferSize = 0; if( mp3->stream.next_frame != 0 ) { int leftOver = mp3->stream.bufend - mp3->stream.next_frame; int i; for( i= 0; i < leftOver; i++ ) mp3->inputBuffer[i] = mp3->stream.next_frame[i]; int readBytes = file_read( mp3->file, mp3->inputBuffer + leftOver, INPUT_BUFFER_SIZE - leftOver); if( readBytes == 0 ) return 0; inputBufferSize = leftOver + readBytes; } else { int readBytes = file_read( mp3->file, mp3->inputBuffer, INPUT_BUFFER_SIZE); if( readBytes == 0 ) return 0; inputBufferSize = readBytes; } mad_stream_buffer( &mp3->stream, mp3->inputBuffer, inputBufferSize ); mp3->stream.error = MAD_ERROR_NONE; } if( mad_frame_decode( &mp3->frame, &mp3->stream ) ) { if( mp3->stream.error == MAD_ERROR_BUFLEN ||(MAD_RECOVERABLE(mp3->stream.error))) continue; else return 0; } else break; } while( 1 ); mad_timer_add( &mp3->timer, mp3->frame.header.duration ); mad_synth_frame( &mp3->synth, &mp3->frame ); mp3->leftSamples = mp3->synth.pcm.length; mp3->offset = 0; return -1; } int NativeMP3Decoder_readSamples(short *target, int size) { MP3FileHandle* mp3 = Handle; //short* target = (short*)env->GetDirectBufferAddress(buffer); int pos=0; int idx = 0; while( idx != size ) { if( mp3->leftSamples > 0 ) { for( ; idx < size && mp3->offset < mp3->synth.pcm.length; mp3->leftSamples--, mp3->offset++ ) { int value = fixedToShort(mp3->synth.pcm.samples[0][mp3->offset]); if( MAD_NCHANNELS(&mp3->frame.header) == 2 ) { value += fixedToShort(mp3->synth.pcm.samples[1][mp3->offset]); value /= 2; } target[idx++] = value; } } else { pos = file_seek( mp3->file, 0, SEEK_CUR); int result = readNextFrame( mp3); if( result == 0 ) return 0; } } /* if(pos > mp3->fileStartPos + mp3->size) { __android_log_print(ANDROID_LOG_INFO, TAG, "*****return -1**\n"); return -1; } */ if( idx > size ) return 0; //return size; return pos; } int NativeMP3Decoder_getAduioSamplerate() { return g_Samplerate; } void NativeMP3Decoder_closeAduioFile() { if( Handle != 0 ) { closeHandle(); Handle = 0; } }
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> #include <stdlib.h> int NativeMP3Decoder_init(char * filepath,unsigned long start/*,unsigned long size*/); int NativeMP3Decoder_readSamples(short *target, int size); void NativeMP3Decoder_closeAduioFile(); int NativeMP3Decoder_getAduioSamplerate();
#define TAG "native_libmad" #include "FileSystem.h" #include <stdlib.h> #include <jni.h> #include <android/log.h> extern int NativeMP3Decoder_readSamples( short *target, int size); extern void NativeMP3Decoder_closeAduioFile(); extern int NativeMP3Decoder_getAduioSamplerate(); extern int NativeMP3Decoder_init(char * filepath,unsigned long start); jint Java_com_conowen_libmad_NativeMP3Decoder_initAudioPlayer(JNIEnv *env, jobject obj, jstring file,jint startAddr) { char* fileString = (*env)->GetStringUTFChars(env,file, NULL); return NativeMP3Decoder_init(fileString,startAddr); } jint Java_com_conowen_libmad_NativeMP3Decoder_getAudioBuf(JNIEnv *env, jobject obj ,jshortArray audioBuf,jint len) { int bufsize = 0; int ret = 0; if (audioBuf != NULL) { bufsize = (*env)->GetArrayLength(env, audioBuf); jshort *_buf = (*env)->GetShortArrayElements(env, audioBuf, 0); memset(_buf, 0, bufsize*2); ret = NativeMP3Decoder_readSamples(_buf, len); (*env)->ReleaseShortArrayElements(env, audioBuf, _buf, 0); } else{ __android_log_print(ANDROID_LOG_DEBUG, TAG, "getAudio failed"); } return ret; } jint Java_com_conowen_libmad_NativeMP3Decoder_getAudioSamplerate() { return NativeMP3Decoder_getAduioSamplerate(); } void Java_com_conowen_libmad_NativeMP3Decoder_closeAduioFile( ) { NativeMP3Decoder_closeAduioFile(); }
2.3、文件读写操作
#include <unistd.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> #include <stdlib.h> #include <fcntl.h> #include"FileSystem.h" int file_open(const char *filename, int flags) { int access; T_pFILE fd = 0; if (flags == _CREATE) { access = O_CREAT | O_TRUNC | O_RDWR; } else if (flags == _WRONLY) { access = O_CREAT | O_TRUNC | O_WRONLY; } else if (flags == _RDONLY){ access = O_RDONLY; } else if (flags == _RDWR){ access = O_RDWR; } else{ return -1; } #ifdef O_BINARY access |= O_BINARY; #endif fd = open(filename, access, 0666); if (fd == -1) return -1; return fd; } int file_read(T_pFILE fd, unsigned char *buf, int size) { return read(fd, buf, size); } int file_write(T_pFILE fd, unsigned char *buf, int size) { return write(fd, buf, size); } int64_t file_seek(T_pFILE fd, int64_t pos, int whence) { if (whence == 0x10000) { struct stat st; int ret = fstat(fd, &st); return ret < 0 ? -1 : st.st_size; } return lseek(fd, pos, whence); } int file_close(T_pFILE fd) { return close(fd); }
头文件
#include <stdlib.h> typedef signed long T_S32; /* signed 32 bit integer */ #define T_pFILE T_S32 #define T_hFILE T_S32 #define _CREATE 0//"wb+"//0 #define _RDONLY 1//"rb" // 1 #define _WRONLY 2//"wb"// 2 #define _RDWR 3//"rb+"// 3 #define _FMODE_READ _RDONLY #define _FMODE_WRITE _WRONLY #define _FMODE_CREATE _CREATE #define _FMODE_OVERLAY _RDWR #define _FSEEK_CURRENT 1 #define _FSEEK_END 2 #define _FSEEK_SET 0 #define _FOPEN_FAIL -1 #define SEEK_CURRENT 1 #define SEEK_CUR 1 #define SEEK_END 2 #define SEEK_SET 0 #define FS_SEEK_SET 0
下一篇博文介绍如何使用这个生成的so库。
见此
http://blog.csdn.net/conowen/article/details/7799155