游戏中录音功能的实现

发表于2017-11-18
评论0 2.2k浏览

正好这次游戏更新版本,有时间来把录音功能做一下,计划把之前游戏的录音功能移过来。

Unity是自带录音功能的,介绍Unity录音功能的文章:http://blog.csdn.net/huutu/article/details/20216613


但是这种录音的方式,文件太大,不适合在移动平台使用。所以像微信 QQ 等 一般都是用AMR格式来作为录音传输。


AMR 音频

AMR是安卓平台录音的文件格式,IOS已经不支持这种格式了,不过好在安卓是开源系统,所以在IOS上可以自己编译lib 来编码 和 解析 AMR。


Android端

开始录音

mRecorder = new MediaRecorder();      
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频来源(MIC表示麦克风)      
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR); // 设置音频输出格式  
mRecorder.setOutputFile(mRecorderFilePath);//设置输出文件   
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置音频编码为AMR  
mRecorder.setMaxDuration(20300);//设置录音时长  
mRecorder.setAudioEncodingBitRate(4000);  
mRecorder.setAudioSamplingRate(8000);//采样率  
try {  
    mRecorder.prepare();  
    mRecorder.start();  
} catch (IllegalStateException e) {  
    // TODO Auto-generated catch block  
    e.printStackTrace();  
} catch (IOException e) {  
    // TODO Auto-generated catch block  
    e.printStackTrace();  
}  

结束录音

mRecorder.stop();  
mRecorder.release();  

播放录音

MediaPlayer mPlayer = new MediaPlayer();  
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener()   
{//播放完毕回调监听  
    @Override  
    public void onCompletion(MediaPlayer arg0)   
    {  
        Toast.makeText(MainActivity.this, "Play End", Toast.LENGTH_LONG).show();  
    }  
    });  
try {  
    Log.i("yhxy", "Play mRecorderFilePath:"+mRecorderFilePath);  
    File f= new File(mRecorderFilePath);    
    if (f.exists() && f.isFile())  
    {    
        Toast.makeText(getApplicationContext(), "filesize:"+f.length(), Toast.LENGTH_LONG).show();  
    }else{    
        Toast.makeText(getApplicationContext(), "file not exist", Toast.LENGTH_LONG).show();  
    }    
    mPlayer.setDataSource(mRecorderFilePath);  
    mPlayer.prepare();  
    //开始播放  
    mPlayer.start();  
} catch (IllegalArgumentException e) {  
    // TODO Auto-generated catch block  
    e.printStackTrace();  
} catch (SecurityException e) {  
    // TODO Auto-generated catch block  
    e.printStackTrace();  
} catch (IllegalStateException e) {  
    // TODO Auto-generated catch block  
    e.printStackTrace();  
} catch (IOException e) {  
    // TODO Auto-generated catch block  
    e.printStackTrace();  
}  


Android 录音 主要注意 权限 配置以及 OS 版本。


Manifest 配置

<uses-permission android:name="android.permission.RECORD_AUDIO"/>  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="21" />  


IOS端

IOS没有对AMR支持,我们需要下载opencore-amr 源代码自行编译成 lib 。


编译opencore-amr

从SourceForge上下载:https://sourceforge.net/projects/opencore-amr/ 


然后创建脚本build.sh
#!/bin/sh  
set -xe  
VERSION="0.1.5" #opencore-amr version  
SDKVERSION="10.3" #xcode Deployment Target  
LIBSRCNAME="opencore-amr"  
CURRENTPATH=`pwd`  
mkdir -p "${CURRENTPATH}/src"  
tar zxvf ${LIBSRCNAME}-${VERSION}.tar.gz -C "${CURRENTPATH}/src"  
cd "${CURRENTPATH}/src/${LIBSRCNAME}-${VERSION}"  
DEVELOPER=`xcode-select -print-path`  
DEST="${CURRENTPATH}/lib-ios"  
mkdir -p "${DEST}"  
ARCHS="armv7 armv7s arm64 i386 x86_64"  
# ARCHS="armv7"  
LIBS="libopencore-amrnb.a libopencore-amrwb.a"  
DEVELOPER=`xcode-select -print-path`  
for arch in $ARCHS; do  
case $arch in  
arm*)  
IOSV="-miphoneos-version-min=7.0"  
if [ $arch == "arm64" ]  
then  
IOSV="-miphoneos-version-min=7.0"  
fi  
echo "Building for iOS $arch ****************"  
SDKROOT="$(xcrun --sdk iphoneos --show-sdk-path)"  
CC="$(xcrun --sdk iphoneos -f clang)"  
CXX="$(xcrun --sdk iphoneos -f clang++)"  
CPP="$(xcrun -sdk iphonesimulator -f clang++)"  
CFLAGS="-isysroot $SDKROOT -arch $arch $IOSV -isystem $SDKROOT/usr/include -fembed-bitcode"  
CXXFLAGS=$CFLAGS  
CPPFLAGS=$CFLAGS  
export CC CXX CFLAGS CXXFLAGS CPPFLAGS  
./configure \  
--host=arm-apple-darwin \  
--prefix=$DEST \  
--disable-shared --enable-static  
;;  
*)  
IOSV="-mios-simulator-version-min=7.0"  
echo "Building for iOS $arch*****************"  
SDKROOT=`xcodebuild -version -sdk iphonesimulator Path`  
CC="$(xcrun -sdk iphoneos -f clang)"  
CXX="$(xcrun -sdk iphonesimulator -f clang++)"  
CPP="$(xcrun -sdk iphonesimulator -f clang++)"  
CFLAGS="-isysroot $SDKROOT -arch $arch $IOSV -isystem $SDKROOT/usr/include -fembed-bitcode"  
CXXFLAGS=$CFLAGS  
CPPFLAGS=$CFLAGS  
export CC CXX CFLAGS CXXFLAGS CPPFLAGS  
./configure \  
--prefix=$DEST \  
--disable-shared  
;;  
esac  
make > /dev/null  
make install  
make clean  
for i in $LIBS; do  
mv $DEST/lib/$i $DEST/lib/$i.$arch  
done  
done  
for i in $LIBS; do  
input=""  
for arch in $ARCHS; do  
input="$input $DEST/lib/$i.$arch"  
done  
lipo -create -output $DEST/lib/$i $input  
done  

将build.sh 和 下载的 opencore 压缩包 放置同一个目录,然后在终端中执行脚本。


执行完毕之后,在 lib-ios 目录中可以看到编译好的lib。


配置Libraries

将上面编译好的lib 拖至Xcode中引用,然后添加 CoreAudio AVFoundation 。


开始录音

-(void)StartRecord:(NSString*)fileName;  
{  
    NSLog(@"StartRecord %@",fileName);  
    self.amrfilename=fileName;  
    //录音前先删除amr文件  
    NSFileManager *pfilemanager=[NSFileManager defaultManager];  
    NSArray* tmpPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    NSString* tmpDocumentDir=[tmpPaths objectAtIndex:0];  
    NSString *ptmpAmrfilepath = [tmpDocumentDir stringByAppendingPathComponent:amrfilename];  
    NSLog(@"amr filepath:%@",ptmpAmrfilepath);  
    BOOL bRet=[pfilemanager fileExistsAtPath:ptmpAmrfilepath];  
    if(bRet)  
    {  
        NSLog(@"remove arm file:%@",ptmpAmrfilepath);  
        [pfilemanager removeItemAtPath:ptmpAmrfilepath error:nil];  
    }  
    NSString* ptmprecordfilename=@"tempRecordVoice.tmp";  
    NSString *ptmprecordfilepath = [tmpDocumentDir stringByAppendingPathComponent:ptmprecordfilename];  
    bRet=[pfilemanager fileExistsAtPath:ptmprecordfilepath];  
    if(bRet)  
    {  
        [pfilemanager removeItemAtPath:ptmprecordfilepath error:nil];  
    }  
    //录音设置  
    NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:  
                                   [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,  
                                   [NSNumber numberWithInt:8000],AVSampleRateKey,  
                                   [NSNumber numberWithInt:1],AVNumberOfChannelsKey,  
                                   [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,  
                                   nil nil];  
    //实例化AVAudioRecorder  
    audioRecorder = [[AVAudioRecorder alloc] initWithURL:  
                     [NSURL fileURLWithPath:ptmprecordfilepath]  
                                                settings:recordSetting  
                                                   error:nil];  
    [audioRecorder setDelegate:self];  
    audioRecorder.meteringEnabled = YES;  
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];  
    [[AVAudioSession sharedInstance] setActive:YES error:nil];  
    NSLog(@"startrecord 1");  
    if ([audioRecorder prepareToRecord])  
    {  
        NSLog(@"startrecord 2");  
        [audioRecorder record];  
    }  
    NSLog(@"startrecord 3");  
}  

停止录音

-(void)endRecord  
{  
    NSLog(@"endRecord");  
    if(audioRecorder==nil)  
    {  
        return;  
    }  
    if ([audioRecorder isRecording]) {  
        [audioRecorder stop];  
    }  
    [[AVAudioSession sharedInstance] setActive:NO error:nil];  
    [audioRecorder release];  
    audioRecorder=nil;  
    //[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];  
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];  
} 

播放录音

- (void)playWithData:(NSData*)data{  
    [self stopPlayQueue];  
    [self setAVAudioSession];  
    NSError *error = nil;  
    audioPlayer = [[AVAudioPlayer alloc] initWithData:data error:&error];  
    if (error) {  
        NSLog(@"Error: %@", [error localizedDescription]);  
    }  
    audioPlayer.delegate = self; // 设置代理  
    [audioPlayer prepareToPlay];// 准备播放  
    [audioPlayer play];// 开始播放  
}  

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引