AVAudioPlayer始终是nill,其isPlaying属性始终为false。播放时文件混合在一起 [英] AVAudioPlayer is always nill and its isPlaying property is always false. The files are mixed while playing

查看:114
本文介绍了AVAudioPlayer始终是nill,其isPlaying属性始终为false。播放时文件混合在一起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

三天后我一直拔出头发来解决这个问题。我已经检查了大量的示例代码,阅读了大量的教程,并使用Google搜索并检查了很多关于stackoverflow的问题和答案,但我仍然无法解决问题。有几个类似的问题,如这个这个但他们也没有任何解决方案。

I've been pulling out my hair past three days to fix this problem. I've checked lots of sample codes, read lots of tutorials, and googled and checked lots and lots of questions and answers on stackoverflow, but I still cannot fix the problem. There are several similar questions like this or this but they don't have any solutions either.

关于我的项目的一点点:

So a little bit about my project:

我有一个 NIKMasterViewController 和一个 NIKDetailViewController 。在第一个中,我在表格视图中有一个音频文件列表;选择一行,它导航到 NIKDetailViewController ,用户可以在其中查看有关该文件的一些信息并播放音频文件。
我在 NIKMasterViewController 中定义了一个 AVAudioPlayer 属性,并设置如下:
NIKMasterViewController.h

I have a NIKMasterViewController and a NIKDetailViewController. In the first one I have a list of audio files in a table view; selecting a row, it navigates to the NIKDetailViewController where the user can see some info about the file and play the audio file. I've defined an AVAudioPlayer property in the NIKMasterViewController and have set it like this: NIKMasterViewController.h:

@property(非原子,强)AVAudioPlayer * audioPlayer;

NIKMasterViewController.m

@synthesize audioPlayer;

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showDetail"])
    {
        NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
        NIKDetailViewController *detailViewController = (NIKDetailViewController *) segue.destinationViewController;
        [detailViewController setAudioPlayer:audioPlayer];

        [detailViewController setFeedEntry:[[[self feedParser] feedItems] objectAtIndex:indexPath.row]];

    } else {
        NSLog(@"Segue Identifier: %@", segue.identifier);
    }

}

这就是 AVAudioPlayer NIKMasterViewController 中。现在在我的 NIKDetailViewController 我有另一个属性 AVAudioPlayer

And that's all about the AVAudioPlayer in NIKMasterViewController. Now in my NIKDetailViewController I have another property of AVAudioPlayer:

NIKDetailViewController.h

@property(非原子,强)AVAudioPlayer * audioPlayer ;

现在我的.m文件中有一个名为 streamAudio 的方法,这是在 viewDidLoad 中调用以准备音频播放,我有 if 条件要求检查 audioPlayer nill 如果没有,如果 audioPlayer.isPlaying 为真,那么它停止 s播放器,但它从未被调用,当我导航回主VC以点击另一行播放另一个文件时,第二个文件开始播放第一个文件正在播放,所有内容都混淆了。

Now in my .m file I have a method called streamAudio which is called in viewDidLoad to prepare the audio playback, and I have an if condition asking to check if the audioPlayer is nill and if not, if the audioPlayer.isPlaying is true so that it stops the player, but it's never called, and when i navigate back to the Master VC to tap on another row to play another file, the second file starts playing while the first file is being played and everything gets mixed up.

任何帮助都会得到真正的赞赏,因为我几乎要在无法修复此问题后停止编程下班后和几天!

Any help will be truly appreciated, since I'm almost about to stop programming after being unable to fix this issue after hours and days!

NIKDetailViewController.m

@synthesize audioPlayer;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        selectedItem = [[NSString alloc]init];
    }
    return self;
}


#pragma mark - Managing the Audio Playback


- (IBAction)togglePlayingState:(id)button
{
    //Handle the button pressing
    [self togglePlayPause];
}



- (void)playAudio
{
    //Play the audio and set the button to represent the audio is playing
    [audioPlayer play];
    [playPauseButton setImage:[UIImage imageNamed:@"player_pause"] forState:UIControlStateNormal];
}

- (void)pauseAudio
{
    //Pause the audio and set the button to represent the audio is paused
    [audioPlayer pause];
    [playPauseButton setImage:[UIImage imageNamed:@"player_play"] forState:UIControlStateNormal];

}

- (void)togglePlayPause
{
    //Toggle if the music is playing or paused
    if (!audioPlayer.playing)
    {
        [self playAudio];
    }
    else if (audioPlayer.playing)
    {
        [self pauseAudio];
    }
}


- (void)streamAudio
{   
    currentFileName = [[feedEntry podcastDownloadURL] lastPathComponent];

    NSString* documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString* path = [documentPath stringByAppendingPathComponent:currentFileName];
    NSURL* audioURL = [NSURL fileURLWithPath: path];

    if (audioPlayer != nil)
    {
        if (audioPlayer.isPlaying)
        {
            [audioPlayer stop];  //THIS IS NEVER CALLED
        }
        audioPlayer = nil;           //THIS IS NEVER CALLED
    }

    audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];

    // Set a timer which keep getting the current music time and update the UISlider in 1 sec interval
    playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateSlider) userInfo:nil repeats:YES];
    // Set the maximum value of the UISlider
    seekSlider.maximumValue = audioPlayer.duration;


    currentTime.text = [NSString stringWithFormat:@"%d:%02d", (int)audioPlayer.currentTime / 60, (int)audioPlayer.currentTime % 60, nil];
    remainingTime.text = [NSString stringWithFormat:@"%d:%02d", (int)(audioPlayer.duration - audioPlayer.currentTime) / 60, (int)(audioPlayer.duration - audioPlayer.currentTime) % 60, nil];

    // Set the valueChanged target
    [seekSlider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
    audioPlayer.delegate = self;
    [audioPlayer prepareToPlay]; //Add the audio to the memory.
}


- (void)updateSlider
{
    // Update the slider about the music time
    seekSlider.value = audioPlayer.currentTime;
}

- (IBAction)sliderChanged:(UISlider *)sender {
    // Fast skip the music when user scrolls the slider
    [audioPlayer stop];
    [audioPlayer setCurrentTime:seekSlider.value];
    audioPlayer.delegate = self;

    [audioPlayer prepareToPlay];
    [audioPlayer play];
}

// Stop the timer when the music is finished (Need to implement the AVAudioPlayerDelegate in the Controller header)
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    // Music completed
    if (flag) {
        [playbackTimer invalidate];
    }
}


- (IBAction)forwardAudio:(id)sender
{
    int currentTime = [audioPlayer currentTime];
    [audioPlayer setCurrentTime:currentTime+10];
}

- (IBAction)rewindAudio:(id)sender
{
    int currentTime = [audioPlayer currentTime];
    [audioPlayer setCurrentTime:currentTime-10];
}


//Make sure we can recieve remote control events
- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    //if it is a remote control event handle it correctly
    if (event.type == UIEventTypeRemoteControl) {
        if (event.subtype == UIEventSubtypeRemoteControlPlay) {
            [self playAudio];
        } else if (event.subtype == UIEventSubtypeRemoteControlPause) {
            [self pauseAudio];
        } else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) {
            [self togglePlayPause];
        }
    }
}

#pragma mark - view life cycle


- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    //Once the view has loaded then we can register to begin recieving controls and we can become the first responder
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    //End recieving events
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
}

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

    [self streamAudio];

    //Make sure the system follows our playback status - to support the playback when the app enters the background mode.
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    [[AVAudioSession sharedInstance] setActive: YES error: nil];

}

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

注意:我我已经尝试将详细VC中的属性设置为但是,我收到警告,并且该属性在我播放文件之前就已释放。

NOTE: I have tried setting the property in the Detail VC as weak but then, I get a warning, and the property is release before I can play the file.

推荐答案

所以...我终于可以通过创建单身来解决这个问题C $ C> audioplayer 。这是如何:

So... I could finally fix this problem by creating a singleton of the audioplayer. This is how:


  1. 首先,我删除了与 audioPlayer 来自我的 NIKMasterViewController 类,其中包含 audioPlayer 声明并将其设置为 prepareForSegue

  2. 我创建了一个名为 NIKAudioPlayer 的新类。

  3. In NIKAudioPlayer.h

  1. First of all, I removed all the code related to the audioPlayer from my NIKMasterViewController class, that includes the audioPlayer declaration and setting it in prepareForSegue.
  2. I created a new class called NIKAudioPlayer.
  3. In NIKAudioPlayer.h:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface NIKAudioPlayer : NSObject <AVAudioPlayerDelegate>
{
    AVAudioPlayer *currentPlayer;
}
@property (nonatomic, strong) AVAudioPlayer *currentPlayer;
+(NIKAudioPlayer *) sharedPlayer;
-(void)playURL:(NSURL*)url;
@end


  • NIKAudioPlayer.m

    #import "NIKAudioPlayer.h"
    
    @implementation NIKAudioPlayer 
    @synthesize currentPlayer;
    
    +(NIKAudioPlayer *) sharedPlayer
    {
        static NIKAudioPlayer* sharedPlayer;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        sharedPlayer = [[NIKAudioPlayer alloc] init];
        });
        return sharedPlayer;
    }
    
    -(void)playURL:(NSURL*)url
    {
        [currentPlayer stop];
        currentPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
        [currentPlayer prepareToPlay];
    }
    
    @end
    


  • 现在到处都是在我需要播放音频文件的代码中(在我的情况下在 NIKDetailViewController 中),我调用 sharedPlayer 来自 NIKAudioPlayer

    [[NIKPlayer sharedPlayer] playURL:audioURL];
    [[NIKPlayer sharedPlayer].currentPlayer prepareToPlay];
    


  • 简而言之,全部替换 audioPlayer NIKDetailViewController 中,带 [NIKPlayer sharedPlayer] .currentPlayer ,甚至投下它并在任何地方使用它:

    To put in a nutshell, replace all audioPlayers in NIKDetailViewController with [NIKPlayer sharedPlayer].currentPlayer, or even cast it and use it everywhere:

    audioPlayer = [NIKPlayer sharedPlayer] .currentPlayer

    这篇关于AVAudioPlayer始终是nill,其isPlaying属性始终为false。播放时文件混合在一起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆