有没有办法记录到音频文件的末尾?我们不能只是暂停录制而不是停止录制,因为用户需要稍后能够返回应用程序并为他们的录制添加更多音频。目前,音频作为 NSData 存储在 CoreData 中。NSDataAppendData
不起作用,因为生成的音频文件仍然报告它仅与原始数据一样长。
另一种可能性是将原始音频文件与新的音频文件一起,并将它们连接成一个音频文件,如果有任何方法可以做到这一点。
有没有办法记录到音频文件的末尾?我们不能只是暂停录制而不是停止录制,因为用户需要稍后能够返回应用程序并为他们的录制添加更多音频。目前,音频作为 NSData 存储在 CoreData 中。NSDataAppendData
不起作用,因为生成的音频文件仍然报告它仅与原始数据一样长。
另一种可能性是将原始音频文件与新的音频文件一起,并将它们连接成一个音频文件,如果有任何方法可以做到这一点。
这可以很容易地使用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;
}];
您可以通过在添加两个文件后创建一个附加两个音频文件并使用的方法AVMutableCompositionTrack
导出合成。exportAsynchronouslyWithCompletionHandler
AVAssetExportSession
请参阅以下链接了解更多详情。
希望这有助于解决您的问题。
我没有完整的代码示例,但扩展音频文件服务可以帮助您连接两个音频文件。在 Xcode 中搜索扩展音频文件服务或访问下面的链接。
我们对我们的应用程序有与 OP 描述的相同的要求,并且遇到了相同的问题(即,如果用户想听她到目前为止录制的内容,则必须停止录制,而不是暂停)。我们的应用程序(项目的 Github存储库)AVQueuePlayer
用于播放和类似于kermitology 的回答的方法来连接部分录音,但有一些显着差异:
最后一项背后的基本原理是简单的录音AVAudioRecorder
只有一个轨道,整个解决方法的主要原因是连接资产中的这些单一轨道(参见附录 3)。那么为什么不使用AVMutableComposition
'insertTimeRange
方法来AVAsset
代替AVAssetTrack
呢?
相关部分:(完整代码)
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
部分持保留意见,而不是在 上使用 KVO AVAssetExportSessionStatus
,但文档很清楚exportAsynchronously
' 的回调块“在写入完成或写入失败时调用”。
附录 2:以防万一有人遇到以下问题AVQueuePlayer
:“一个 AVPlayerItem 不能与多个 AVPlayer 实例关联”
附录 3:除非您以立体声录制,但据我所知,移动设备只有一个输入。此外,使用精美的音频混合还需要使用AVCompositionTrack
. 一个好的 SO 线程:Proper AVAudioRecorder Settings for Recording Voice?