Playing audio with controls in iOS

19,064

Solution 1

I would suggest you create a global player object. Because right now, each time you push this view on the navigation controller you create a new one. This also means that you do not have any reference to the previous player (and can not stop it). So: declare the AVAudioPlayer one step higher (in the tableview). Now, when you select a row in it, assign it to a property of this new view (the one you linked).

Do you work with storyboard? Then you have to implement the method prepareForSegue:. Give your segue on the storyboard an identifier (like showPlayer and check for that with if (segue.identifier isEqualToString:@"showPlayer")).

Now in your viewDidLoad do a check if audioPlayer is nil. If you

- (void)viewDidLoad
{
    [super viewDidLoad];
    if (audioPlayer == nil)
    {
         // your init (audioplayer =[[AVAudioPlayer ...
    }
    else
    {
         if (audioPlayer.isPlaying)
         {
             // i.e. pause the player or do other stuff
         }
    }
}

Hope this helps you.

Also: Do not post images of code. Just insert the code in your response or post it to a site like pastebin.com and link that page in your question. This makes it easier for others to respond and make suggestions how to alter your code.

In response to your comment: The relevant stuff should be: In AudioTableViewController.h:

@property (strong, nonatomic) AVAudioPlayer *audioPlayer;

In AudioTableViewController.m:

@synthesize audioPlayer;
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Navigation logic may go here. Create and push another view controller.

    NSUInteger row = indexPath.row;

    if (row == 0) 
    {
        self.audio1DetailViewController =[[Audio1DetailViewController alloc] initWithNibName:@"Audio1DetailViewController" bundle:nil]; 
        self.audio1DetailViewController.title = @"Audio";
        self.audio1DetailViewController.audioPlayer = self.audioPlayer;
        [self.navigationController pushViewController:self.audio1DetailViewController animated:YES];   
        [self.audio1DetailViewController release];
        ...

Audio1DetailViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"001Fatiha" ofType:@"MP3"]];

    NSError *error;

    // this is the important part: if we already have something playing, stop it!
    if (audioPlayer != nil)
    {
        [audioPlayer stop];
    }
    // everything else should happen as normal.
    audioPlayer = [[AVAudioPlayer alloc]
               initWithContentsOfURL:url
               error:&error];

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    [[AVAudioSession sharedInstance] setActive: YES error: nil];

Solution 2

to stop 2 songs playing at a same time you should do this:

- (void)viewWillDisappear:(BOOL)animated
{
    audioPlayer = nil;
 volumeControl = nil;
}

for more help in playing audio you can use apple sample code:

http://developer.apple.com/library/ios/#samplecode/avTouch/Introduction/Intro.html

Solution 3

Surprisingly the MPMoviePlayerController also plays MP3 player with controls!!!

self.moviePlayer=[[MPMoviePlayerController alloc] initWithContentURL:url];
[self.moviePlayer.view setFrame:CGRectMake(0, 0, self.videoContainer.bounds.size.width, self.videoContainer.bounds.size.height)];
self.moviePlayer.controlStyle=MPMovieControlStyleDefault;
[self.videoContainer addSubview:self.moviePlayer.view];
[self.moviePlayer prepareToPlay];
[self.moviePlayer play];
Share:
19,064
iHilas
Author by

iHilas

Updated on June 26, 2022

Comments

  • iHilas
    iHilas almost 2 years

    I've made an app with tab bar,nav bar and table view.

    In the table view you can choose to listen to some audio.

    New view opens and there I have some controls like: play,pause,volume slider, progress slider, label with the current time.

    It works, but not perfect. I can play the audio, I can pause the audio, I can also use the slider to skip forward or back. But now:

    When I hit the Back button on the navbar, the song keeps playing. That's ok, but when I go back to the view again, the timer and the slider reset themselves. I can't pause the song, just need to wait util it stops playing.

    Also, when I hit play, go back to the table view, choose another file to play, the first file won't stop playing.

    Here is the Audio1DetailViewController.h code:

         #import <UIKit/UIKit.h>
         #import <AVFoundation/AVFoundation.h>
    
         @interface Audio1DetailViewController: UIViewController <AVAudioPlayerDelegate> {
    
         IBOutlet UISlider *volumeControl; 
         IBOutlet UILabel  *timerLabel; 
         IBOutlet UISlider *progressBar; 
    
         AVAudioPlayer *audioPlayer;
         NSTimer *playbackTimer; 
    
         }
    
         @property (nonatomic, retain) IBOutlet UISlider *volumeControl;
         @property (nonatomic, retain) IBOutlet UILabel *timerLabel;
         @property (nonatomic, retain) IBOutlet UISlider *progressBar;
         @property (nonatomic, retain) NSTimer  *playbackTimer; 
         @property (nonatomic, retain) AVAudioPlayer *audioPlayer;
         -(IBAction) playAudio;
         -(IBAction) stopAudio;
         -(IBAction) adjustVolume;
         -(IBAction) sliderChanged;
    
         @end
    

    Here is the Audio1DetailViewController.m code:

         #import "Audio1DetailViewController.h"
    
    
         @implementation Audio1DetailViewController
    
         @synthesize volumeControl, timerLabel, playbackTimer, progressBar, audioPlayer;
    
         -(void)playAudio
         {
         playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
         target:self
         selector:@selector(updateTime)
         userInfo:nil
         repeats:YES];
         [audioPlayer play];
         }
         -(void)stopAudio
         {
         [playbackTimer invalidate];
         [audioPlayer stop];
         }
         -(void)adjustVolume
         {
         if (audioPlayer != nil)
         {
         audioPlayer.volume = volumeControl.value;
         }
         }
    
         -(void)updateTime
         {
         float minutes = floor(audioPlayer.currentTime/60);
         float seconds = audioPlayer.currentTime - (minutes * 60);
    
         float duration_minutes = floor(audioPlayer.duration/60);
         float duration_seconds = 
         audioPlayer.duration - (duration_minutes * 60);
    
         NSString *timeInfoString = [[NSString alloc] 
         initWithFormat:@"%0.0f.%0.0f / %0.0f.%0.0f",
         minutes, seconds, 
         duration_minutes, duration_seconds];
    
         timerLabel.text = timeInfoString;
         [timeInfoString release];
         }
    
         - (void)viewDidLoad {
         [super viewDidLoad];
         NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
         pathForResource:@"001Fatiha"
         ofType:@"MP3"]];
    
         NSError *error;
         audioPlayer = [[AVAudioPlayer alloc]
         initWithContentsOfURL:url
         error:&error];
    
         [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
         [[AVAudioSession sharedInstance] setActive: YES error: nil];
    
         if (error)
         {
         NSLog(@"Error in audioPlayer: %@", 
         [error localizedDescription]);
         } else {
         audioPlayer.delegate = self;
         [audioPlayer prepareToPlay];
         }
    
    
         playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
         selector:@selector(updateSlider) userInfo:nil repeats:YES];
    
         progressBar.maximumValue = audioPlayer.duration;
         // Set the valueChanged target
         [progressBar addTarget:self action:@selector(sliderChanged:) forControlEvents:
         UIControl EventValueChanged];
         }
    
         - (void)updateSlider {
         // Update the slider about the music time
         progressBar.value = audioPlayer.currentTime;
         }
    
         - (IBAction)sliderChanged:(UISlider *)sender {
         // Fast skip the music when user scroll the UISlider
         [audioPlayer stop];
         [audioPlayer setCurrentTime:progressBar.value];
         [audioPlayer prepareToPlay];
         [audioPlayer play];
         }
    
         -(void)audioPlayerDidFinishPlaying:
         (AVAudioPlayer *)player successfully:(BOOL)flag
         {
         }
    
    
         -(void)audioPlayerDecodeErrorDidOccur:
         (AVAudioPlayer *)player error:(NSError *)error
         {
         }
         -(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
         {
         }
         -(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
         {
         }
    
         -(void)viewDidUnload {
         audioPlayer = nil;
         volumeControl = nil;
    
         }
    
         -(void)dealloc {
         [audioPlayer release];
         [volumeControl release];
         [playbackTimer release];
         [super dealloc];
         }
    
         @end
    

    Here is the AudioTableViewController.h

         #import <UIKit/UIKit.h>
    
         @class Audio1DetailViewController;
    
    
         @interface AudioTableViewController : UITableViewController 
         <UITableViewDelegate,UITableViewDataSource>{
    
    
         IBOutlet UITableView *audioTableView;
         NSMutableArray *audioArray;
         Audio1DetailViewController *audio1DetailViewController;        
         }
    
         @property (nonatomic, retain) NSMutableArray *audioArray;
         @property (nonatomic, retain) Audio1DetailViewController *audio1DetailViewController;
    
         @end
    

    And the AudioTableViewController.m

        #import "AudioTableViewController.h"
        #import "Audio1DetailViewController.h"
    
        #import "DEQAppDelegate.h"
    
        @implementation AudioTableViewController
        @synthesize audioArray;
        @synthesize audio1DetailViewController;
    
        - (id)initWithStyle:(UITableViewStyle)style
        {
            self = [super initWithStyle:style];
            if (self) {
                // Custom initialization
        {
            return self;
        }
    
        - (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.
        }
    
        #pragma mark - View lifecycle
    
        - (void)viewDidLoad{
            [super viewDidLoad];
            self.title = NSLocalizedString(@"Audio", @"Audio");
            self.audioArray = [[NSArray alloc] initWithObjects:
                              @"1. Het Begin",  @"2. De Mensen", //etcetera                      
                      nil];
            // Uncomment the following line to preserve selection between presentations.
            self.clearsSelectionOnViewWillAppear = NO;
    
            // Uncomment the following line to display an Edit button in the navigation bar for this
            view controller.
            // self.navigationItem.rightBarButtonItem = self.editButtonItem;
        }
    
        - (void)viewDidUnload
        {
            [super viewDidUnload];
            // Release any retained subviews of the main view.
            self.audioArray = nil;
        }
    
        - (void)viewWillAppear:(BOOL)animated
        {
            [super viewWillAppear:animated];
        }
    
        - (void)viewDidAppear:(BOOL)animated
        {
            [super viewDidAppear:animated];
        }
    
        - (void)viewWillDisappear:(BOOL)animated
        {
            [super viewWillDisappear:animated];
        }
    
        - (void)viewDidDisappear:(BOOL)animated
        {
            [super viewDidDisappear:animated];
        }
    
        - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
        {
            // Return YES for supported orientations
            return (interfaceOrientation == UIInterfaceOrientationPortrait);
        }
    
        #pragma mark - Table view data source
    
        - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
        {
        #warning Potentially incomplete method implementation.
            // Return the number of sections.
            return 1;
        }
    
        - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
        {
        #warning Incomplete method implementation.
            // Return the number of rows in the section.
            return [self.audioArray count];
        }
    
        - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
          (NSIndexPath *)indexPath
        {
            static NSString *CellIdentifier = @"Cell";
    
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
            if (cell == nil) {
                cell = [[[UITableViewCell alloc] initWithStyle:
            UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier] autorelease];
            }
    
            // Configure the cell...
            cell.textLabel.text = [self.audioArray objectAtIndex:[indexPath row]];
    
            return cell;
        }
    
        #pragma mark - Table view delegate
    
        - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
        {
            // Navigation logic may go here. Create and push another view controller.
    
            NSUInteger row = indexPath.row;
    
            if (row == 0) 
            {
         Audio1DetailViewController *audio1DetailViewController =[[Audio1DetailViewController alloc]
         initWithNibName:@"Audio1DetailViewController" bundle:nil]; 
         audio1DetailViewController.title = @"Audio";
         [self.navigationController pushViewController:audio1DetailViewController animated:YES];   
         [audio1DetailViewController release];
    
            }
    
            if (row == 1) 
            {
                //etcetera
            }
    
        }
        - (void)dealloc{
            [audioArray release];
            [super dealloc];
        }
    
        @end
    
  • iHilas
    iHilas over 12 years
    Hey @pxldlx,tnx for the answer.I have tried but failed.It sounds just simple what you said,and it makes sense.But I just don't get it,I think. I have declared the AVAudioPlayer in the AudioTableViewController.h from the Audio1DetailViewController.h Then I moved the whole code(not the viewDidLoad part)from the Audio1DetailViewController.m to AudioTableViewController.m I also changed the File'sOwner in the xib file.I can play the song,but not by pressing the play button,but by moving the progress slider. I tried some other things but after 2days,I gave it up and undid all the changes.
  • David Langer
    David Langer over 12 years
    @iHilas Mh, ok. Without looking at your code I can not really help you that much. Can you edit your original question and add some code (mainly that code that you already posted as an image but this time as text please :) ). Oh and one thing: I don't think you have to change that much. Everything stays the same, but you change the "owner" of your audioPlayer to that superview (in the sense that this controller has a <code>strong</code> property). When you traverse to that new controller you hand the reference audioPlayer to the new controller.
  • iHilas
    iHilas over 12 years
    Still no success. I have done what you told me, but it keeps playing 2 songs at the same time, the timer and the slider also keep resetting themselves. I just can't figure it out!
  • Alex Stone
    Alex Stone over 9 years
    This is the correct and most straightforward way to deal with playback with controls. In iOS8, Apple added even more flexibility to what controls you show (airplay, etc)