AVAudioRecorder / AVAudioPlayer - 将录音附加到文件

时间:2011-01-10 18:07:44

标签: iphone ios cocoa-touch avaudioplayer avaudiorecorder

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

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

4 个答案:

答案 0 :(得分:7)

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

}];

答案 1 :(得分:4)

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

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

AVAssetExportSession Class Reference

Creating New Assets

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

答案 2 :(得分:1)

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

Apple documentation

答案 3 :(得分:0)

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

  • Swift
  • 中实施
  • 多个录音合并为一个
  • 没有混乱曲目

最后一项背后的理由是,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隐式暗示为没有继承箭头的超类。)

enter image description here

附录1:我对switch部分有所保留,而不是在AVAssetExportSessionStatus上使用KVO,但文档清楚exportAsynchronously&#39 ; s回调块"在写入完成时或在写入失败的情况下被调用"。

附录2:万一有人遇到AVQueuePlayer问题:'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'

附录3 :除非您是以立体声录制,但据我所知,移动设备只有一个输入。此外,使用花哨的音频混合还需要使用AVCompositionTrack。一个好的SO线程:正确的AVAudioRecorder Settings for Recording Voice?