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 并使用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?

【讨论】:

以上是关于AVAudioRecorder / AVAudioPlayer - 将录音附加到文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 AVAudio 循环播放 mp3

致命错误:使用 AVAudio 展开可选值时意外发现 nil

在应用程序终止之前获取 AVAudio 录音 URL

首次加载时 AVAudio Player 出现延迟

使其他音频安静的音效 iOS AVAudio

iPhone App:不恢复 AVAudio Player 中的音频会话