【问题标题】:AVAudioRecorder / AVAudioPlayer - append recording to fileAVAudioRecorder / AVAudioPlayer - 将录音附加到文件
【发布时间】:2011-01-10 18:07:44
【问题描述】:

有没有办法记录到音频文件的末尾?我们不能只是暂停录制而不是停止录制,因为用户需要稍后能够返回应用程序并为他们的录制添加更多音频。目前,音频作为 NSData 存储在 CoreData 中。 NSData 的AppendData 不起作用,因为生成的音频文件仍然报告它只有原始数据的长度。

如果有任何方法,另一种可能性是将原始音频文件与新音频文件一起,并将它们连接到一个音频文件中。

【问题讨论】:

标签: iphone ios cocoa-touch avaudioplayer avaudiorecorder


【解决方案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 并使用AVAssetExportSessionexportAsynchronouslyWithCompletionHandler 方法导出合成来附加两个音频文件。

    请参阅以下链接了解更多详情。

    AVAssetExportSession Class Reference

    Creating New Assets

    希望这有助于解决您的问题。

    【讨论】:

    • 这个答案虽然不完全完整,但当我遇到同样的问题时,肯定会让我走上正确的道路。
    • 我在下面添加了完整的代码,但正是这个答案为我指明了正确的方向。
    • @siddharth 您从哪里获得@disanji.net 的文档?它看起来像苹果文档的副本,但我在苹果官方文档中没有看到该部分。
    【解决方案3】:

    我没有完整的代码示例,但扩展音频文件服务可以帮助您连接两个音频文件。在 Xcode 中搜索扩展音频文件服务或访问下面的链接。

    Apple documentation

    【讨论】:

    • 该文档中没有任何内容提及附加音频文件。
    • 老实说,这是一个有趣的参考,如果你不想连接到准备好的文件,但想写入现有文件,它的工作原理,它很棒(就像在苹果录音机中一样!)
    【解决方案4】:

    我们对应用程序的要求与 OP 描述的相同,并且遇到了相同的问题(即,如果用户想听她录制的内容,则必须停止而不是暂停录制观点)。我们的应用程序 (project's Github repo) 使用 AVQueuePlayer 进行播放,并使用类似于 kermitology's answer 的方法连接部分录音,但有一些显着差异:

    • Swift 中实现
    • 多个录音合并为一个
    • 不要弄乱曲目

    最后一项背后的基本原理是 AVAudioRecorder 的简单录音将只有一个轨道,而整个解决方法的主要原因是将这些单一轨道连接到资产中(参见 附录 3 )。那么为什么不改用AVMutableCompositioninsertTimeRange 方法,它采用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?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-21
      • 1970-01-01
      • 1970-01-01
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多