游戏中录音功能的实现
发表于2017-11-18
正好这次游戏更新版本,有时间来把录音功能做一下,计划把之前游戏的录音功能移过来。
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];// 开始播放
}
