7

私のプログラムでは、ユーザーが次のことができるようにしたい:

  • 彼の声を録音し、
  • 記録プロセスを一時停止し、
  • 彼が録音したものを聞く
  • そして、録音を続けます。

AVAudioRecorder と AVAudioPlayer で録音と再生ができるようになりました。しかし、録音し、録音を一時停止してから再生しようとすると、再生部分がエラーなしで失敗します。

再生されない理由は、オーディオ ファイルがまだ保存されておらず、メモリなどに残っているためだと推測できます。

一時停止した録画を再生する方法はありますか? あるなら方法教えてください

私はxcode 4.3.2を使用しています

4

3 に答える 3

10

録音を再生する場合は、ファイルをAVAudioPlayerインスタンスにロードする前に、録音を停止する必要があります。

録音の一部を再生できるようにしたい場合は、それを聞いた後で録音にさらに追加するか、途中で録音と言います。そうすると、問題が発生します。

新しいオーディオファイルを作成してから、それらを組み合わせる必要があります。

これが私の解決策でした:

// Generate a composition of the two audio assets that will be combined into
// a single track
AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack* audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
                                                                 preferredTrackID:kCMPersistentTrackID_Invalid];

// grab the two audio assets as AVURLAssets according to the file paths
AVURLAsset* masterAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:self.masterFile] options:nil];
AVURLAsset* activeAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:self.newRecording] options:nil];

NSError* error = nil;

// grab the portion of interest from the master asset
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, masterAsset.duration)
                    ofTrack:[[masterAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
                     atTime:kCMTimeZero
                      error:&error];
if (error)
{
    // report the error
    return;
}

// append the entirety of the active recording
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, activeAsset.duration)
                    ofTrack:[[activeAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
                     atTime:masterAsset.duration
                      error:&error];

if (error)
{
    // report the error
    return;
}

// now export the two files
// create the export session
// no need for a retain here, the session will be retained by the
// completion handler since it is referenced there

AVAssetExportSession* exportSession = [AVAssetExportSession
                                       exportSessionWithAsset:composition
                                       presetName:AVAssetExportPresetAppleM4A];
if (nil == exportSession)
{
    // report the error
    return;
}


NSString* combined = @"combined file path";// create a new file for the combined file

// configure export session  output with all our parameters
exportSession.outputURL = [NSURL fileURLWithPath:combined]; // output path
exportSession.outputFileType = AVFileTypeAppleM4A; // output file type

[exportSession exportAsynchronouslyWithCompletionHandler:^{

    // export status changed, check to see if it's done, errored, waiting, etc
    switch (exportSession.status)
    {
        case AVAssetExportSessionStatusFailed:
            break;
        case AVAssetExportSessionStatusCompleted:
            break;
        case AVAssetExportSessionStatusWaiting:
            break;
        default:
            break;
    }
    NSError* error = nil;

    // your code for dealing with the now combined file
}];

私はこの作品を完全に信用することはできませんが、他のいくつかの入力からまとめられました:

AVAudioRecorder/AVAudioPlayer-ファイルに録音を追加します

(現在、他のリンクが見つかりません)

于 2012-11-18T07:50:10.143 に答える
3

OPが説明したのと同じ要件がアプリにあり、同じ問題に遭遇しました(つまり、ユーザーがその時点までに録音したものを聞きたい場合は、録音を一時停止するのではなく停止する必要があります)。私たちのアプリ (プロジェクトの Github リポジトリ) はAVQueuePlayer再生に使用し、kermitology の回答に似た方法を使用して、部分的な記録を連結しますが、いくつかの顕著な違いがあります。

  • スイフトで実装
  • 複数の録音を 1 つに連結します
  • トラックをいじらない

最後の項目の背後にある理論的根拠は、単純な録音にAVAudioRecorderは 1 つのトラックがあり、この回避策全体の主な理由は、アセット内のこれらの単一トラックを連結することです (補遺 3を参照)。代わりにAVMutableCompositioninsertTimeRangeメソッドを使用しないのはなぜですか。AVAssetAVAssetTrack

関連部分: (完全なコード)

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 on を使用する代わりに、この部分に関して留保がありましたが、ドキュメントでは、 「書き込みが完了したとき、または書き込みが失敗したときに呼び出される」というコールバック ブロックAVAssetExportSessionStatusが明確です。exportAsynchronously

補遺 2:誰かが問題を抱えている場合に備えてAVQueuePlayer: 「AVPlayerItem は AVPlayer の複数のインスタンスに関連付けることはできません」

補遺 3:ステレオで録音している場合を除きますが、私の知る限り、モバイル デバイスには入力が 1 つあります。また、ファンシー オーディオ ミキシングを使用するには、 も使用する必要がありますAVCompositionTrack。良い SO スレッド:音声を録音するための適切な AVAudioRecorder 設定?

于 2018-04-23T05:20:33.590 に答える
1

RecordAudioViewController.h

 #import <UIKit/UIKit.h>
 #import <AVFoundation/AVFoundation.h>
 #import <CoreAudio/CoreAudioTypes.h>

   @interface record_audio_testViewController : UIViewController <AVAudioRecorderDelegate> {

IBOutlet UIButton * btnStart;
IBOutlet UIButton * btnPlay;
IBOutlet UIActivityIndicatorView * actSpinner;
BOOL toggle;

//Variables setup for access in the class:
NSURL * recordedTmpFile;
AVAudioRecorder * recorder;
NSError * error;

 }

 @property (nonatomic,retain)IBOutlet UIActivityIndicatorView * actSpinner;
 @property (nonatomic,retain)IBOutlet UIButton * btnStart;
 @property (nonatomic,retain)IBOutlet UIButton * btnPlay;

 - (IBAction) start_button_pressed;
 - (IBAction) play_button_pressed;
 @end

RecordAudioViewController.m

  @synthesize actSpinner, btnStart, btnPlay;
   - (void)viewDidLoad {
    [super viewDidLoad];

//Start the toggle in true mode.
toggle = YES;
btnPlay.hidden = YES;

//Instanciate an instance of the AVAudioSession object.
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
//Setup the audioSession for playback and record. 
//We could just use record and then switch it to playback leter, but
//since we are going to do both lets set it up once.
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error];
//Activate the session
[audioSession setActive:YES error: &error];

  }


 - (IBAction)  start_button_pressed{

if(toggle)
{
    toggle = NO;
    [actSpinner startAnimating];
    [btnStart setTitle:@"Stop Recording" forState: UIControlStateNormal ];  
    btnPlay.enabled = toggle;
    btnPlay.hidden = !toggle;

    //Begin the recording session.
    //Error handling removed.  Please add to your own code.

    //Setup the dictionary object with all the recording settings that this 
    //Recording sessoin will use
    //Its not clear to me which of these are required and which are the bare minimum.
    //This is a good resource: http://www.totodotnet.net/tag/avaudiorecorder/
    NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init];
    [recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
    [recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey]; 
    [recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];

    //Now that we have our settings we are going to instanciate an instance of our recorder instance.
    //Generate a temp file for use by the recording.
    //This sample was one I found online and seems to be a good choice for making a tmp file that
    //will not overwrite an existing one.
    //I know this is a mess of collapsed things into 1 call.  I can break it out if need be.
    recordedTmpFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: @"%.0f.%@", [NSDate timeIntervalSinceReferenceDate] * 1000.0, @"caf"]]];
    NSLog(@"Using File called: %@",recordedTmpFile);
    //Setup the recorder to use this file and record to it.
    recorder = [[ AVAudioRecorder alloc] initWithURL:recordedTmpFile settings:recordSetting error:&error];
    //Use the recorder to start the recording.
    //Im not sure why we set the delegate to self yet.  
    //Found this in antother example, but Im fuzzy on this still.
    [recorder setDelegate:self];
    //We call this to start the recording process and initialize 
    //the subsstems so that when we actually say "record" it starts right away.
    [recorder prepareToRecord];
    //Start the actual Recording
    [recorder record];
    //There is an optional method for doing the recording for a limited time see 
    //[recorder recordForDuration:(NSTimeInterval) 10]

}
else
{
    toggle = YES;
    [actSpinner stopAnimating];
    [btnStart setTitle:@"Start Recording" forState:UIControlStateNormal ];
    btnPlay.enabled = toggle;
    btnPlay.hidden = !toggle;

    NSLog(@"Using File called: %@",recordedTmpFile);
    //Stop the recorder.
    [recorder stop];
}
  }

  - (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
  }

  -(IBAction) play_button_pressed{

//The play button was pressed... 
//Setup the AVAudioPlayer to play the file that we just recorded.
AVAudioPlayer * avPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:recordedTmpFile error:&error];
[avPlayer prepareToPlay];
[avPlayer play];

  }

   - (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
//Clean up the temp file.
NSFileManager * fm = [NSFileManager defaultManager];
[fm removeItemAtPath:[recordedTmpFile path] error:&error];
//Call the dealloc on the remaining objects.
[recorder dealloc];
recorder = nil;
recordedTmpFile = nil;
  }


  - (void)dealloc {
[super dealloc];
  }

 @end

RecordAudioViewController.xib

2つのボタンを取ります。1 つは録音開始用、もう 1 つは録音再生用です。

于 2012-06-13T10:16:28.087 に答える