AVAudioRecorder / AVAudioPlayer - добавить запись в файл

Есть ли способ записи в конец аудиофайла? Мы не можем просто приостановить запись, а не остановить ее, потому что пользователь должен иметь возможность вернуться в приложение позже и добавить больше звука к своей записи. В настоящее время аудио хранится в CoreData как NSData. NSData-х AppendData не работает, потому что результирующий аудиофайл по-прежнему сообщает, что он содержит только исходные данные.

Другая возможность - взять оригинальный аудиофайл вместе с новым и объединить их в один аудиофайл, если есть какой-либо способ сделать это.

4 ответа

Это можно сделать довольно легко, используя 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;

}];

Вы можете добавить два аудио файла, создав AVMutableCompositionTrack после добавления двух файлов и экспорта композиции с помощью exportAsynchronouslyWithCompletionHandler метод AVAssetExportSession,

Пожалуйста, обратитесь к ссылкам ниже для более подробной информации.

Ссылка на класс AVAssetExportSession

Создание новых активов

Надеюсь, это поможет решить вашу проблему.

У нас были те же требования к нашему приложению, что и у описанного OP, и мы столкнулись с теми же проблемами (т. Е. Запись должна быть остановлена, а не приостановлена, если пользователь хочет прослушать то, что она записала до этого момента). Наше приложение ( репозиторий Github проекта) использует AVQueuePlayer для воспроизведения и метода, аналогичного ответу кермитологии, для объединения частичных записей с некоторыми заметными отличиями:

  • реализовано в Swift
  • объединяет несколько записей в одну
  • не возиться с треками

Основанием для последнего пункта является то, что простые записи с AVAudioRecorder будет иметь одну дорожку, и главная причина для всего этого обходного пути состоит в объединении этих отдельных дорожек в активах (см. Приложение 3). Так почему бы не использовать AVMutableComposition"s 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 часть вместо использования КВО на AVAssetExportSessionStatus, но документы ясно, что exportAsynchronouslyБлок обратного вызова "вызывается, когда запись завершена или в случае сбоя записи".

Приложение 2: На всякий случай, если у кого-то есть проблемы с AVQueuePlayer: "AVPlayerItem не может быть связан с более чем одним экземпляром AVPlayer"

Приложение 3: Если вы не записываете в стерео, но мобильные устройства имеют один вход, насколько я знаю. Кроме того, использование необычного микширования звука также потребует использования AVCompositionTrack, Хороший SO поток: правильные настройки AVAudioRecorder для записи голоса?

У меня нет полного примера кода, но службы расширенных аудиофайлов могут помочь вам объединить два аудиофайла. Ищите Расширенные Сервисы Аудио Файла в XCode или посетите ссылку ниже.

Документация Apple

Другие вопросы по тегам