单击“播放”按钮

时间:2015-12-03 10:45:32

标签: ios objective-c avfoundation avaudioplayer

我正在使用" AVFoundation.h"编写音频播放器。我有更新进度条的问题,因此当我点击播放按钮时,我的应用程序会出错。我附上了两个代码示例和错误报告。任何人都可以解决这个问题吗?

-(void)updateProgress {
NSInteger durationMinutes = [self.audioPlayer duration] / 60;
NSInteger durationSeconds = [self.audioPlayer duration]  - durationMinutes * 60;

NSInteger currentTimeMinutes = [self.audioPlayer currentTime] / 60;
NSInteger currentTimeSeconds = [self.audioPlayer currentTime]  - currentTimeMinutes * 60;
NSString *progressString = [NSString stringWithFormat:@"%d:%02d / %d:%02d", currentTimeMinutes, currentTimeSeconds, durationMinutes, durationSeconds];
self.timeLabel.text = progressString;

self.progressBar.progress = [self.audioPlayer currentTime] / [self.audioPlayer duration];

NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInt:currentTimeSeconds];
NSNumber *numDurationSeconds = [NSNumber numberWithInt:durationSeconds];

NSString *songTitle = [self.selectedFilePath lastPathComponent];
NSString *artistName = @"MyPlayer";
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"placeholder"]];


MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:@[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:@[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
[infoCenter setNowPlayingInfo:infoDict];        }

按下Build&运行应用程序在模拟器中成功启动,我已经拍摄了2张活动控制台的图像 1.点击播放按钮之前。

  1. 点击播放按钮后。当应用程序崩溃时。
  2. enter image description here

    现在请建议我此时应该做些什么?这样我的应用程序就能顺利开始...... 谢谢 法伊兹。

    按照Losiowaty的指示回答最后一天。那些黄色问题被删除了,但是当我点击播放按钮时,我的编程仍会出现同样的错误。 enter image description here

    这次我上传完整的代码并突出显示我认为错误发生的一些事情。 请看一下我的mainwviewcontroller.m类代码。

        @interface MainViewController ()
        @end
        @implementation MainViewController
        @synthesize audioPlayer;
        - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib
        NSError *error = nil;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
        if (error == nil) {
            NSLog(@"audio session initialized successfully");
        } else {
            NSLog(@"error initializing audio session: %@", [error description]);
        }
    
    
        [audioPlayer setDelegate:self];
        MPVolumeView *volumeView = [ [MPVolumeView alloc] init] ;
    
        [volumeView setFrame:self.airPlayView.bounds];
        [self.airPlayView addSubview:volumeView];
    
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    
        [self becomeFirstResponder];
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(caughtInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
    }
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    -(BOOL)canBecomeFirstResponder
    {
        return YES;
    }
    
    -(void)dealloc
    {
        [self resignFirstResponder];
    
        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
    }
    
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if ([[segue identifier] isEqualToString:@"showFilePicker"]) {
            UINavigationController *navigationController = (UINavigationController *)segue.destinationViewController;
            FileViewController *fileViewController = (FileViewController *)navigationController.topViewController;
            fileViewController.delegate = self;
        }
    }
    
    #pragma mark - file picker delegate methods
    
    -(void)cancel
    {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    -(void)didFinishWithFile:(NSString *)filePath
    {
        NSError *error = nil;
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
    
    
        self.selectedFilePath = filePath;
    
        NSString *relativeFilePath = [documentsDirectory stringByAppendingPathComponent:filePath];
    
        NSURL *fileURL = [NSURL fileURLWithPath:relativeFilePath];
    
        self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
        self.audioPlayer.delegate = self;
    
        if (error == nil) {
            NSLog(@"audio player initialized successfully");
    
            self.titleLabel.text = self.selectedFilePath;
    
            self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
    
            NSString *songTitle = [filePath lastPathComponent];
            NSString *artistName = @"MyPlayer";
            MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"placeholder"]];
    
            MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
            NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:@[songTitle, artistName, albumArt] forKeys:@[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork]];
            [infoCenter setNowPlayingInfo:infoDict];
    
            [self play:nil];
    
    
        } else {
            NSLog(@"error initializing audio player: %@", [error description]);
        }
    
    
        //dismiss the file picker
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    -(IBAction)play:(id)sender
    {
        if ([self.audioPlayer isPlaying]) {
            [self.audioPlayer pause];
            [self.playButton setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
            [self.timer invalidate];
            [animation stopAnimating];
    
        } else {
            [self.audioPlayer play];
            [self.playButton setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
            self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
    
            animation.animationImages = [NSArray arrayWithObjects:
                                         [UIImage imageNamed:@"animation1.png"],
                                         [UIImage imageNamed:@"animation2.png"],
                                         [UIImage imageNamed:@"animation3.png"],nil];
    
            [animation setAnimationRepeatCount:2000];
            animation.animationDuration = 0.5;
            [animation startAnimating];
    
        }
        self.playbackInterrupted = NO;
    }
    
    -(IBAction)skipForward:(id)sender
    {
        if ([self.audioPlayer isPlaying]) {
            NSTimeInterval desiredTime = self.audioPlayer.currentTime + 15.0f;
            if (desiredTime < self.audioPlayer.duration) {
                self.audioPlayer.currentTime = desiredTime;
            }
        }
    }
    
    -(IBAction)skipBackward:(id)sender
    {
        if ([self.audioPlayer isPlaying]) {
            NSTimeInterval desiredTime = self.audioPlayer.currentTime - 15.0f;
            if (desiredTime < 0) {
                self.audioPlayer.currentTime = 0.0f;
            } else {
                self.audioPlayer.currentTime = desiredTime;
            }
    
        }
    }
    
    #pragma mark - Timer delegate
    
    -(void)updateProgress
    {
        NSInteger durationMinutes = [self.audioPlayer duration] / 60;
        NSInteger durationSeconds = [self.audioPlayer duration]  - durationMinutes * 60;
    
        NSInteger currentTimeMinutes = [self.audioPlayer currentTime] / 60;
        NSInteger currentTimeSeconds = [self.audioPlayer currentTime]  - currentTimeMinutes * 60;
        NSString *progressString = [NSString stringWithFormat:@"%ld:%02ld / %ld:%02ld", currentTimeMinutes,currentTimeSeconds, durationMinutes, durationSeconds];
        self.timeLabel.text = progressString;
    
        self.progressBar.progress = [self.audioPlayer currentTime] / [self.audioPlayer duration];
    
        NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInteger:currentTimeSeconds];
        NSNumber *numDurationSeconds = [NSNumber numberWithInteger:durationSeconds];
    
        NSString *songTitle = [self.selectedFilePath lastPathComponent];
        NSString *artistName = @"MyPlayer";
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"placeholder"]];
    
    
        MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
        NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:@[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:@[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
            [infoCenter setNowPlayingInfo:infoDict];
    
    }
    
    #pragma mark - AVAudioPlayer delegate methods
    
    -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
    {
        if (flag) {
            [self.playButton setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
            [self.timer invalidate];
            [animation stopAnimating];
        }
    }
    
    #pragma mark - Remote control
    
    -(void)remoteControlReceivedWithEvent:(UIEvent *)event
    {
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPlay:
            case UIEventSubtypeRemoteControlPause:
            case UIEventSubtypeRemoteControlTogglePlayPause:
                [self play:nil];
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                [self skipForward:nil];
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                [self skipBackward:nil];
                break;
            default:
                break;
        }
    }
    
    #pragma mark - audio interruption
    
    -(void)caughtInterruption:(NSNotification *)notification
    {
        NSDictionary *userInfo = notification.userInfo;
    
        NSNumber *type =[userInfo objectForKey:AVAudioSessionInterruptionTypeKey];
        if ([type integerValue] == AVAudioSessionInterruptionTypeBegan) {
            if (self.audioPlayer.playing) {
                [self.audioPlayer pause];
                [animation stopAnimating];
                self.playbackInterrupted = YES;
            }
        } else {
            if (self.audioPlayer.playing == NO && self.playbackInterrupted == YES) {
                [self.audioPlayer play];
                [animation startAnimating];
                self.playbackInterrupted = NO;
            }
        }
    }
    
    #pragma mark - route changed
    
    -(void)routeChanged:(NSNotification *)notification
    {
        NSDictionary *userInfo = notification.userInfo;
    
        NSNumber *reason =[userInfo objectForKey:AVAudioSessionRouteChangeReasonKey];
        switch ([reason integerValue]) {
            case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
                [self.audioPlayer stop];
                [animation stopAnimating];
                break;
            case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            case AVAudioSessionRouteChangeReasonWakeFromSleep:
                [self.audioPlayer pause];
                [animation stopAnimating];
                break;
            default:
                break;
        }
    
    }
    
    
    @end
    

    以上代码没有错误&amp;很干净,一切都清楚地提到,我正在使用4个按钮,

    1. 适用于Play&amp;暂停

    2. 寻求前瞻

    3. 寻求落后

    4. 用于输入文件目录以进行音频文件拣选

    5. 当我按下第四个按钮时,它准备进入另一个视图以选择音频文件。

      - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
      {
          if ([[segue identifier] isEqualToString:@"showFilePicker"]) {
              UINavigationController *navigationController = (UINavigationController *)segue.destinationViewController;
              FileViewController *fileViewController = (FileViewController *)navigationController.topViewController;
              fileViewController.delegate = self;
          }
      }
      

      我需要完成两件事

      我需要做的第一件事就是,

      1. 我不想进入下一个视图,因为我正在测试我的应用程序进入模拟器,其中没有物理音频文件我可以放置或定位在模拟器中,因此我需要避免这个事情只是为了我自己的测试目的。 因此我愿意将音频mp3文件添加到NSBundle中,并且当我按下播放按钮文件开始播放时想播放此文件。然后再次按下时暂停。支付代码暂停很干净很好。但是对于初始化文件路径,我认为我已经通过替换上面的视图来通过以下代码来加载viewDidload方法来初始化文件路径。

        - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:@"Nameofflie" ofType:@"mp3"];
        NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
        
        
        NSError *error = nil;
        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
           if (error == nil) {
            NSLog(@"audio session initialized successfully");
        } else {
            NSLog(@"error initializing audio session: %@", [error description]);
        }
        
        
        [audioPlayer setDelegate:self];
        MPVolumeView *volumeView = [ [MPVolumeView alloc] init] ;
        
        [volumeView setFrame:self.airPlayView.bounds];
        [self.airPlayView addSubview:volumeView];
        
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
        
        [self becomeFirstResponder];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(caughtInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
        

        }

      2. 此代码也运行&amp;编译得很好但是当按下播放按钮时发生同样的错误。所以请建议我在哪里放置以下行来播放从NSBudle放置的MP3音乐文件。

        NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:@"rabne" ofType:@"mp3"];
        NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
        
        1. 这一点与我愿意完成的第1点完全不同。测试成功后,播放那个NSBundle音频MP3文件。 我想再次使用我的早期代码,因为最终用户没有使用我的模拟器,因此对于最终用户,我希望我已经在Mainviewcontroller.m类上面完成了相同的选项,即。用户必须按第4个数字按钮才能访问其设备的文档目录文件路径。在我的代码中运行得很好。但是我想指出或者需要解决的问题是,如果有人直接按下第一个播放按钮而不按第四个按钮进行操作和放大选择音频文件时,应出现一个警报视图,其中首先按第四个按钮选择文件,然后单击播放按钮。我愿意在我的代码中使用它。

1 个答案:

答案 0 :(得分:0)

根据屏幕截图#2中的例外情况,您看起来正在尝试将nil对象插入到数组中。将一些对象插入数组的唯一位置是:

NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:@[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:@[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];

第二个数组,带有键的数组,看起来不错,因为它只包含系统提供的功能。另一方面,第一个对象有两个对象,而不是nil:songTitlealbumArt

nil的原因是:

    如果songTitlenil ,则
  1. self.selectedFilePath可能为nil
  2. albumArt - 我不完全确定,但如果找不到您的图片,最终可能会nil
  3. 请确保这两个不是nil,一切都应该正常。

    关于你的警告,这两个:

    NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInt:currentTimeSeconds];
    NSNumber *numDurationSeconds = [NSNumber numberWithInt:durationSeconds];
    

    可以通过更改为[NSNumber numberWithInteger:]来修复,并且是由NSInteger typedef long而不是int而导致的。

    上的警告
    NSString *progressString = [NSString stringWithFormat:@"%d:%02d / %d:%02d", currentTimeMinutes, currentTimeSeconds, durationMinutes, durationSeconds];
    

    几乎是由同一件事造成的。 %d期望intNSIntegerlong。将%d更改为%ld将解决此问题。

    我仍然坚信这些并不会导致崩溃,特别是基于抛出的异常,在说明发生的事情时非常简单。

    提供的代码确认了我的假设 - 崩溃发生的原因是self.selectedFilePath nil updateProgress方法导致songTitle nilself.selectedFilePath。为什么会这样?设置didFinishWithFile:的提供代码中唯一的位置是FileViewController方法,我假设它是viewDidLoad的委托方法。如果你没有展示它并在那里选择了某些东西,那么就不会调用那个方法。

    现在,如果您想将其设置为进行测试,最简单的方法是在NSError *error; NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:@"rabne" ofType:@"mp3"]; NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath]; audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error]; if (error == nil) { self.selectedFilePath = @"test file"; // <<-- IMPORTANT self.titleLabel.text = self.selectedFilePath; } else { NSLog(@"error initializing audio player: %@", [error description]); } 中添加此内容:

    [audioPlayer setDelegate:self];

    就在self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];之上。这应该可以解决所有问题。

    旁注:我还会从didFinishWithFile:方法移除此行play: - 您还在self.selectedFilePath != nil方法中设置了一个计时器,这样做一次似乎更安全。

    关于第2点 - 我可以给你一个提示,你知道在<%=时是否选择了一个文件,并查看UIAlertViewController class。剩下的工作留给你,因为它不是原始问题的一部分,与解决崩溃无关。此外,你不会学到任何东西:)