10

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

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

4

4 回答 4

8

这可以很容易地使用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;

}];
于 2013-04-16T15:26:36.300 回答
4

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

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

AVAssetExportSession 类参考

创建新资产

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

于 2012-01-31T12:29:16.110 回答
1

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

苹果文档

于 2011-01-13T10:43:23.237 回答
1

我们对我们的应用程序有与 OP 描述的相同的要求,并且遇到了相同的问题(即,如果用户想听她到目前为止录制的内容,则必须停止录制,而不是暂停)。我们的应用程序(项目的 Github存储库)AVQueuePlayer用于播放和类似于kermitology 的回答的方法来连接部分录音,但有一些显着差异:

  • Swift中实现
  • 将多个录音连接成一个
  • 不弄乱曲目

最后一项背后的基本原理是简单的录音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?

于 2018-04-23T05:26:48.130 回答