EZRecorder在iPhone X上调用ExtAudioFileWrite时崩溃

时间:2018-07-25 21:35:34

标签: ios iphone-x audiokit ezaudio ios11.4

我有一个示例应用程序,该应用程序使用AudioKit记录音频并显示该音频数据的波形。此示例应用程序有两个viewControllers,其vc根目录是一个空白页面,带有一个按钮,该按钮会将用户带到音频记录页面。

由于某种原因,仅在iPhone X(iOS 11.4.1)上,在录制音频时,如果我按下导航栏上的“后退”按钮(左上角),然后再次尝试录制该应用程序将崩溃。 / p>

特别是当记录器的方法appendDataFromBufferList: withBufferSize:调用ExtAudioFileWrite(self.info->extAudioFileRef, bufferSize, bufferList)时,应用程序似乎崩溃了。控制台中显示的错误消息是:

testAudioCrash(1312,0x16e203000)malloc: * **对象0x109803a00错误:释放的对象的校验和不正确-释放后可能已修改了对象。 * **在malloc_error_break中设置一个断点进行调试

我经历了僵尸分析,泄漏分析,逐步执行逻辑和堆栈操作,但是我似乎无法弄清为什么会发生这种情况。

下面,我提供了测试应用程序的代码以及堆栈和控制台输出的屏幕截图。任何帮助弄清楚为什么它崩溃的人将不胜感激。不幸的是,这种崩溃也不是100%可再现的,这让我更加困惑。

以下代码的注释: .h文件中没有自定义代码,因此我没有提供。每个视图控制器都有带有用户界面组件的xib文件。它们非常简单,因此我也没有提供有关这些信息的信息,尽管我没有提供任何人要求的关于它们的信息的问题。我还可以压缩项目并共享,如果有人认为有必要的话。

复制步骤: 1)启动应用 2)点击录制音频按钮 3)点击录音按钮 4)点击导航栏上的后退按钮

5)重复步骤2-4,直到发生崩溃

AppDelegate.m代码:

#import "AppDelegate.h"
#import "testViewController.h"

@interface AppDelegate ()
@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    testViewController* rootVC = [[testViewController alloc] initWithNibName: @"testViewController" bundle: NSBundle.mainBundle];
    UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController: rootVC];
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
    return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}


- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}


@end

testViewController.m代码:

#import "testViewController.h"
#import "testSecondViewController.h"

@interface testViewController ()

@end

@implementation testViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)AudioRecording:(id)sender
{
    testSecondViewController* sVC = [[testSecondViewController alloc] initWithNibName: @"testSecondViewController" bundle: NSBundle.mainBundle];
    [self.navigationController pushViewController: sVC animated: YES];
}

@end

testSecondViewController.m代码:

#import "testSecondViewController.h"
@import AudioKit;
@import AudioKitUI;

@interface testSecondViewController () <EZMicrophoneDelegate, EZRecorderDelegate>
@property (nonatomic, strong) EZRecorder* recorder;
@property (nonatomic, strong) EZMicrophone* mic;
@property (nonatomic, strong) EZAudioPlayer* player;
@property (strong, nonatomic) IBOutlet EZAudioPlot *audioPlot;
@property (nonatomic, strong) NSURL *finishedRecordingURL;
@property (atomic, assign) BOOL isRecording;

@end

@implementation testSecondViewController

- (void)dealloc
{
    if(_isRecording) [self pauseRecording: _mic];
    if(_recorder) [self finalizeAudioFile: _recorder];
    _recorder.delegate = nil;
    _mic.delegate = nil;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [EZAudioUtilities setShouldExitOnCheckResultFail: NO];

    [self setupUI];
    [self setupConfig];
    [self audioKitSetup];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark UI Methods
-(void)setupUI
{
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style: UIBarButtonItemStylePlain target: nil action:@selector(cancelButtonClicked)];
    [self configureWaveFormViewForAudioInput];
}

-(void)setupConfig
{
    [self initializeMic];
    [self initializeRecorder];
}

-(void)initializeMic
{
    self.mic = [[EZMicrophone alloc] initWithMicrophoneDelegate: self];
    self.isRecording = NO;
}

-(void)initializeRecorder
{
    NSURL *fileUrl = [self testFilePathURL];
    self.finishedRecordingURL = fileUrl;

    self.recorder = [[EZRecorder alloc] initWithURL: fileUrl clientFormat: [self.mic audioStreamBasicDescription] fileType: EZRecorderFileTypeM4A delegate: self];
}

#pragma mark - Utils
- (NSArray *)applicationDocuments
{
  return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
}


- (NSString *)applicationDocumentsDirectory
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    return basePath;
}

- (NSURL *)testFilePathURL
{
    self.finishedRecordingURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@",
                               [self applicationDocumentsDirectory],
                               @"test2.m4a"]];

    if (self.finishedRecordingURL && [[NSFileManager defaultManager] fileExistsAtPath:self.finishedRecordingURL.path])
    {
        NSError *error;
        [[NSFileManager defaultManager] removeItemAtURL:self.finishedRecordingURL error:&error];
        if(error){
            printf("%s", error.description);
        }
    }

    return self.finishedRecordingURL;
}

#pragma mark AudioKit Util methods
- (void) audioKitSetup
{
    [AKSettings setDefaultToSpeaker: YES];
    [AKSettings setAudioInputEnabled: YES];
    [AKSettings setPlaybackWhileMuted: YES];
    [AKSettings setSampleRate: 44100];
    [AKSettings setChannelCount: 1];
}

- (void) configureWaveFormViewForAudioInput
{
//    self.audioPlot.gain = 6;
//    self.audioPlot.color = [UIColor blueColor];
    self.audioPlot.plotType = EZPlotTypeRolling;
//    self.audioPlot.shouldFill = YES;
//    self.audioPlot.shouldMirror = YES;
    [self.view addSubview: self.audioPlot];
    self.audioPlot.clipsToBounds = YES;
}

- (IBAction)startRecording:(id)sender
{
    if (!self.mic)
    {
        self.mic = [EZMicrophone microphoneWithDelegate: self];
    }

    if (!self.recorder)
    {
        if (self.finishedRecordingURL && [[NSFileManager defaultManager] fileExistsAtPath:self.finishedRecordingURL.path])
        {
            self.recorder = [EZRecorder recorderWithURL: self.finishedRecordingURL clientFormat: [self.mic audioStreamBasicDescription] fileType: EZRecorderFileTypeM4A delegate: self];
        }
        else
        {
            self.recorder = [EZRecorder recorderWithURL: [self testFilePathURL] clientFormat: [self.mic audioStreamBasicDescription] fileType: EZRecorderFileTypeM4A delegate: self];
            self.finishedRecordingURL = self.recorder.url;
        }
    }

    [self.mic startFetchingAudio];
    self.isRecording = YES;
}

- (IBAction)pauseRecording:(id)sender
{
    [self.mic stopFetchingAudio];
    self.isRecording = NO;
}

- (void) finalizeAudioFile: (EZRecorder*) recorder
{
    if (self.isRecording)
    {
        [self.mic stopFetchingAudio];
    }

    [recorder closeAudioFile];
}

- (IBAction)cancelButtonClicked:(id)sender
{
        if(self.isRecording)
    {
        [self pauseRecording: self.mic];
    }

    UIAlertController *alert = [UIAlertController alertControllerWithTitle: @"Delete recording?" message:@"Would you like to delete your audio recording and stop recording?" preferredStyle: UIAlertControllerStyleAlert];

        UIAlertAction* yesButton = [UIAlertAction
                                actionWithTitle:@"Discard"
                                style:UIAlertActionStyleDefault
                                handler:^(UIAlertAction * action) {

                                    [self finalizeAudioFile: self.recorder];

                                    NSError *error;
                                    [[NSFileManager defaultManager] removeItemAtURL:self.finishedRecordingURL error:&error];
                                    if(error){
                                        printf("%s", error.description);
                                    }

                                     [self dismissViewControllerAnimated:YES completion:NULL];
                                }];

        UIAlertAction* noButton = [UIAlertAction
                               actionWithTitle:@"Cancel"
                               style:UIAlertActionStyleDefault
                               handler:^(UIAlertAction * action) {
                                   [alert dismissViewControllerAnimated:YES completion: nil];
                               }];

                                   [alert addAction:yesButton];
    [alert addAction:noButton];

    [self presentViewController:alert animated:YES completion:nil];
}

#pragma mark - EZMicrophone Delegate methods
- (void)  microphone:(EZMicrophone *)microphone
    hasAudioReceived:(float **)buffer
      withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
    __weak typeof (self) weakling = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakling.audioPlot updateBuffer:buffer[0]
                          withBufferSize:bufferSize];
    });
}

- (void)  microphone:(EZMicrophone *)microphone
       hasBufferList:(AudioBufferList *)bufferList
      withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
    if (self.isRecording)
    {
        [self.recorder appendDataFromBufferList:bufferList
                                 withBufferSize:bufferSize];
    }
}

- (void)microphone:(EZMicrophone *)microphone changedPlayingState:(BOOL)isPlaying
{
    self.isRecording = isPlaying;
}

@end

图片: enter image description here

enter image description here

0 个答案:

没有答案