AVAudioRecorder / AVAudioPlayer - 将录音附加到文件
Posted
技术标签:
【中文标题】AVAudioRecorder / AVAudioPlayer - 将录音附加到文件【英文标题】:AVAudioRecorder / AVAudioPlayer - append recording to file 【发布时间】:2011-01-10 18:07:44 【问题描述】:有没有办法记录到音频文件的末尾?我们不能只是暂停录制而不是停止录制,因为用户需要稍后能够返回应用程序并为他们的录制添加更多音频。目前,音频作为 NSData 存储在 CoreData 中。 NSData 的AppendData
不起作用,因为生成的音频文件仍然报告它只有原始数据的长度。
如果有任何方法,另一种可能性是将原始音频文件与新音频文件一起,并将它们连接到一个音频文件中。
【问题讨论】:
看看我下面的代码。这是我在生产应用程序中使用的。 你可以找到我的完整代码:***.com/a/49136633/2244094 【参考方案1】:这可以使用AVMutableComposionTrack insertTimeRange:ofTrack:atTime:error
轻松完成。代码有些冗长,但实际上只是 4 个步骤:
// Create a new audio track we can append to
AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack* appendedAudioTrack =
[composition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
// Grab the two audio tracks that need to be appended
AVURLAsset* originalAsset = [[AVURLAsset alloc]
initWithURL:[NSURL fileURLWithPath:originalAudioPath] options:nil];
AVURLAsset* newAsset = [[AVURLAsset alloc]
initWithURL:[NSURL fileURLWithPath:newAudioPath] options:nil];
NSError* error = nil;
// Grab the first audio track and insert it into our appendedAudioTrack
AVAssetTrack *originalTrack = [originalAsset tracksWithMediaType:AVMediaTypeAudio];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, originalAsset.duration);
[appendedAudioTrack insertTimeRange:timeRange
ofTrack:[originalTrack objectAtIndex:0]
atTime:kCMTimeZero
error:&error];
if (error)
// do something
return;
// Grab the second audio track and insert it at the end of the first one
AVAssetTrack *newTrack = [newAsset tracksWithMediaType:AVMediaTypeAudio];
timeRange = CMTimeRangeMake(kCMTimeZero, newAsset.duration);
[appendedAudioTrack insertTimeRange:timeRange
ofTrack:[newTrack objectAtIndex:0]
atTime:originalAsset.duration
error:&error];
if (error)
// do something
return;
// Create a new audio file using the appendedAudioTrack
AVAssetExportSession* exportSession = [AVAssetExportSession
exportSessionWithAsset:composition
presetName:AVAssetExportPresetAppleM4A];
if (!exportSession)
// do something
return;
NSString* appendedAudioPath= @""; // make sure to fill this value in
exportSession.outputURL = [NSURL fileURLWithPath:appendedAudioPath];
exportSession.outputFileType = AVFileTypeAppleM4A;
[exportSession exportAsynchronouslyWithCompletionHandler:^
// exported successfully?
switch (exportSession.status)
case AVAssetExportSessionStatusFailed:
break;
case AVAssetExportSessionStatusCompleted:
// you should now have the appended audio file
break;
case AVAssetExportSessionStatusWaiting:
break;
default:
break;
NSError* error = nil;
];
【讨论】:
【参考方案2】:您可以通过在添加两个文件后创建一个AVMutableCompositionTrack
并使用AVAssetExportSession
的exportAsynchronouslyWithCompletionHandler
方法导出合成来附加两个音频文件。
请参阅以下链接了解更多详情。
AVAssetExportSession Class Reference
Creating New Assets
希望这有助于解决您的问题。
【讨论】:
这个答案虽然不完全完整,但当我遇到同样的问题时,肯定会让我走上正确的道路。 我在下面添加了完整的代码,但正是这个答案为我指明了正确的方向。 @siddharth 您从哪里获得@disanji.net 的文档?它看起来像苹果文档的副本,但我在苹果官方文档中没有看到该部分。【参考方案3】:我没有完整的代码示例,但扩展音频文件服务可以帮助您连接两个音频文件。在 Xcode 中搜索扩展音频文件服务或访问下面的链接。
Apple documentation
【讨论】:
该文档中没有任何内容提及附加音频文件。 老实说,这是一个有趣的参考,如果你不想连接到准备好的文件,但想写入现有文件,它的工作原理,它很棒(就像在苹果录音机中一样!)【参考方案4】:我们对应用程序的要求与 OP 描述的相同,并且遇到了相同的问题(即,如果用户想听她录制的内容,则必须停止而不是暂停录制观点)。我们的应用程序 (project's Github repo) 使用 AVQueuePlayer
进行播放,并使用类似于 kermitology's answer 的方法连接部分录音,但有一些显着差异:
最后一项背后的基本原理是 AVAudioRecorder
的简单录音将只有一个轨道,而整个解决方法的主要原因是将这些单一轨道连接到资产中(参见 附录 3 )。那么为什么不改用AVMutableComposition
的insertTimeRange
方法,它采用AVAsset
而不是AVAssetTrack
?
相关部分:(full code)
import UIKit
import AVFoundation
class RecordViewController: UIViewController
/* App allows volunteers to record newspaper articles for the
blind and print-impaired, hence the name.
*/
var articleChunks = [AVURLAsset]()
func concatChunks()
let composition = AVMutableComposition()
/* `CMTimeRange` to store total duration and know when to
insert subsequent assets.
*/
var insertAt = CMTimeRange(start: kCMTimeZero, end: kCMTimeZero)
repeat
let asset = self.articleChunks.removeFirst()
let assetTimeRange =
CMTimeRange(start: kCMTimeZero, end: asset.duration)
do
try composition.insertTimeRange(assetTimeRange,
of: asset,
at: insertAt.end)
catch
NSLog("Unable to compose asset track.")
let nextDuration = insertAt.duration + assetTimeRange.duration
insertAt = CMTimeRange(start: kCMTimeZero, duration: nextDuration)
while self.articleChunks.count != 0
let exportSession =
AVAssetExportSession(
asset: composition,
presetName: AVAssetExportPresetAppleM4A)
exportSession?.outputFileType = AVFileType.m4a
exportSession?.outputURL = /* create URL for output */
// exportSession?.metadata = ...
exportSession?.exportAsynchronously
switch exportSession?.status
case .unknown?: break
case .waiting?: break
case .exporting?: break
case .completed?: break
case .failed?: break
case .cancelled?: break
case .none: break
/* Clean up (delete partial recordings, etc.) */
这张图帮助我了解了期望什么以及从哪里继承的问题。 (NSObject
隐含为没有继承箭头的超类。)
附录 1: 我对 switch
部分而不是在 AVAssetExportSessionStatus
上使用 KVO 持保留意见,但文档很清楚 exportAsynchronously
的回调块“在编写时被调用已完成或在写入失败的情况下”。
附录2:以防万一有人对AVQueuePlayer
有问题:'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'
附录 3: 除非您以立体声录制,但据我所知,移动设备只有一个输入。此外,使用精美的音频混合还需要使用AVCompositionTrack
。一个好的 SO 线程:正确的AVAudioRecorder Settings for Recording Voice?
【讨论】:
以上是关于AVAudioRecorder / AVAudioPlayer - 将录音附加到文件的主要内容,如果未能解决你的问题,请参考以下文章