Iphone AVAudioRecorder/AVAudioPlayer-将录制附加到文件

Iphone AVAudioRecorder/AVAudioPlayer-将录制附加到文件,iphone,ios,cocoa-touch,avaudioplayer,avaudiorecorder,Iphone,Ios,Cocoa Touch,Avaudioplayer,Avaudiorecorder,有没有办法录制到音频文件的末尾?我们不能只是暂停录制,而不是停止录制,因为用户需要能够稍后返回应用程序并向其录制添加更多音频。目前,音频作为NSData存储在CoreData中。NSData的AppendData不起作用,因为生成的音频文件仍然报告其长度与原始数据一样长 另一种可能是将原始音频文件与新的音频文件一起连接成一个音频文件(如果有任何方法的话)。我没有完整的代码示例,但扩展音频文件服务可以帮助您连接两个音频文件。在Xcode中搜索扩展音频文件服务或访问下面的链接 在添加两个文件并使用A

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


另一种可能是将原始音频文件与新的音频文件一起连接成一个音频文件(如果有任何方法的话)。

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


在添加两个文件并使用
AvassetExportHandler
exportAsynchronouslyWithCompletionHandler
方法导出合成后,您可以通过创建
AVMutableCompositionTrack
附加两个音频文件

请参考以下链接了解更多详细信息


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

使用
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;

}];

我们对我们的应用程序的要求与OP描述的相同,并且遇到了相同的问题(即,如果用户想要收听到目前为止她录制的内容,必须停止录制,而不是暂停)。我们的应用程序()使用
AVQueuePlayer
进行播放,并使用类似的方法连接部分录制,但有一些显著的区别:

  • 在Swift中实施
  • 将多个录制连接为一个
  • 不要弄乱曲目
最后一项的基本原理是,使用
AVAudioRecorder
的简单录音将有一个音轨,而整个解决方案的主要原因是将这些单一音轨连接到资产中(请参见附录3)。那么,为什么不使用
AVMutableComposition
insertTimeRange
方法,它采用
AVAsset
而不是
AVAssetTrack

相关部分:()

这个图表帮助我了解了什么是预期的,从哪里继承的。(
NSObject
隐式表示为没有继承箭头的超类。)

附录1:我对
开关
部分有所保留,而不是在
AVAssetExportSessionStatus
上使用KVO,但文档明确指出,
异步导出
的回调块“在写入完成或写入失败时被调用”

附录2:以防万一有人对
AVQueuePlayer有问题


附录3:除非您使用立体声录音,但据我所知,移动设备只有一个输入。此外,使用奇特的音频混音还需要使用
AVCompositionTrack
。一个好的SO线程:正确的

这个答案,虽然不完全完整,但当我提出同样的问题时,它确实让我走上了正确的道路。我在下面添加了完整的代码,但正是这个答案为我指明了正确的方向。@siddharth你从哪里获得的文档@disanji.net?它看起来像是苹果文档的副本,但我在苹果官方文档中没有看到这一部分。看看下面我的代码。这就是我在一个生产应用程序中使用的。你可以找到我的完整代码:文档中没有任何引用附加音频文件。老实说,这是一个有趣的引用,如果你不想连接到准备好的文件,但想写入现有的文件,它的工作非常棒(就像在苹果录音机中!)
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.) */
    }