[iOS] Come creare un Player Audio con AV Foundation framework

In questo post, riporto dei riferimenti per la creazione di un Player Audio in iOS, grazie al framework AV Foundation, interfaccia in Objective-C per il playing, il recording e l’editing degli asset audio, partendo da una libreria trovata su GitHub di Matt Donnelly e customizzata all’uopo.

/System/Library/Frameworks/AVFoundation.framework

Viene usato anche il framework AudioToolBox, per il supporto al background playing (playback), alla gestione dei remote controls (per il controllo della riproduzione quando l’app è in background) e della sessione audio.

/System/Library/Frameworks/AudioToolbox.framework

Entrambi i framework si trovano nella SDK di iOS ed occorre importarli nel nostro progetto (Build Phases>>Link binary with libraries):

Nelle nostre interfacce/classi occorrerà importare i frameworks come segue:

#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
Per brevità, riporto soltanto le interfacce delle classi che implementano la logica del player. Le implementation e tutti gli assets li trovate allegati al presente post in un file zip. Se vi occorre un esempio eseguibile, vi consiglio di scaricare il progetto MDAudioPlayerController di Matt Connelly.
Ecco gli screenshot della mia con il Player Audio:
[portfolio_slideshow]
  • MDAudioPlayerController: è il core del Player e conterrà tutta la sua logica di controllo, nonché il design dell’interfaccia grafica del player (che qui non è stata fatta con uno XIB file).
//
//  MDAudioPlayerController.h
//

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

extern CGContextRef MyCreateBitmapContext(int pixelsWide, int pixelsHigh);
extern CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh);
extern void interruptionListenerCallback (void *userData, UInt32 interruptionState);

@interface MDAudioPlayerController : UIViewController <AVAudioPlayerDelegate, UITableViewDelegate, UITableViewDataSource>
{
	NSMutableArray		*soundFiles;
	NSString			*soundFilesPath;
	NSUInteger			selectedIndex;

	AVAudioPlayer		*player;

	CAGradientLayer		*gradientLayer;

	UIButton			*playButton;
	UIButton			*pauseButton;
	UIButton			*nextButton;
	UIButton			*previousButton;
	UIButton			*toggleButton;
	UIButton			*repeatButton;
	UIButton			*shuffleButton;
	UILabel				*currentTime;
	UILabel				*duration;
	UILabel				*titleLabel;
	UILabel				*artistLabel;
	UILabel				*albumLabel;
	UILabel				*indexLabel;
	UISlider			*volumeSlider;
	UISlider			*progressSlider;

	UITableView			*songTableView;

	UIButton			*artworkView;
	UIImageView			*reflectionView;
	UIView				*containerView;
	UIView				*overlayView;

	NSTimer				*updateTimer;

	BOOL				interrupted;
	BOOL				repeatAll;
	BOOL				repeatOne;
	BOOL				shuffle;
}

@property (nonatomic, retain) NSMutableArray *soundFiles;
@property (nonatomic, copy) NSString *soundFilesPath;

@property (nonatomic, retain) AVAudioPlayer *player;

@property (nonatomic, retain) CAGradientLayer *gradientLayer;

@property (nonatomic, retain) UIButton *playButton;
@property (nonatomic, retain) UIButton *pauseButton;
@property (nonatomic, retain) UIButton *nextButton;
@property (nonatomic, retain) UIButton *previousButton;
@property (nonatomic, retain) UIButton *toggleButton;
@property (nonatomic, retain) UIButton *repeatButton;
@property (nonatomic, retain) UIButton *shuffleButton;

@property (nonatomic, retain) UILabel *currentTime;
@property (nonatomic, retain) UILabel *duration;
@property (nonatomic, retain) UILabel *indexLabel;;
@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *artistLabel;
@property (nonatomic, retain) UILabel *albumLabel;

@property (nonatomic, retain) UISlider *volumeSlider;
@property (nonatomic, retain) UISlider *progressSlider;

@property (nonatomic, retain) UITableView *songTableView;

@property (nonatomic, retain) UIButton *artworkView;
@property (nonatomic, retain) UIImageView *reflectionView;
@property (nonatomic, retain) UIView *containerView;
@property (nonatomic, retain) UIView *overlayView;

@property (nonatomic, retain) NSTimer *updateTimer;

@property (nonatomic, assign) BOOL interrupted;
@property (nonatomic, assign) BOOL repeatAll;
@property (nonatomic, assign) BOOL repeatOne;
@property (nonatomic, assign) BOOL shuffle;

- (MDAudioPlayerController *)initWithSoundFiles:(NSMutableArray *)songs atPath:(NSString *)path andSelectedIndex:(int)index;
- (void)dismissAudioPlayer;
- (void)showSongFiles;
- (void)showOverlayView;

- (BOOL)canGoToNextTrack;
- (BOOL)canGoToPreviousTrack;

- (void)play;
- (void)previous;
- (void)next;
- (void)volumeSliderMoved:(UISlider*)sender;
- (void)progressSliderMoved:(UISlider*)sender;

- (void)toggleShuffle;
- (void)toggleRepeat;

- (void)selectRowAtIndexPath:(int) index;

@end
  • MDAudioFile: conterrà le informazioni di ogni brano audio (titolo, path o filename e informazioni)
//
//  MDAudioFile.h
//

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

@interface MDAudioFile : NSObject
{
	NSURL			*filePath;
    NSString        *songTitle;
	NSDictionary	*fileInfoDict;
}

@property (nonatomic, retain) NSURL *filePath;
@property (nonatomic, retain) NSString *songTitle;
@property (nonatomic, retain) NSDictionary *fileInfoDict;

- (MDAudioFile *)initWithPath:(NSURL *)path;
- (NSDictionary *)songID3Tags;
- (NSString *)title;
- (NSString *)artist;
- (NSString *)album;
- (float)duration;
- (NSString *)durationInMinutes;
- (UIImage *)coverImage;

@end
  • MDAudioPlayerTableViewCell: ha il compito di disegnare le celle della tabella con la lista degli mp3 con tutte le loro informazioni
//
//  MDAudioPlayerTableViewCell.h
//

#import <Foundation/Foundation.h>

@interface MDAudioPlayerTableViewCell : UITableViewCell
{
	UIView				*contentView;

	NSString			*title;
	NSString			*number;
	NSString			*duration;

	BOOL				isEven;
	BOOL				isSelectedIndex;
}

@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *number;
@property (nonatomic, copy) NSString *duration;

@property (nonatomic, assign) BOOL isEven;
@property (nonatomic, assign) BOOL isSelectedIndex;

- (void)drawContentView:(CGRect)r;

@end
Per poter utilizzare il nostro player occorre inserire il seguente codice nel nostro controller (nel mio caso è un UITableViewController), magari facendolo scattare al touch up inside di un elemento presente nella lista dei nostri mp3:
    NSMutableArray *songs = [[NSMutableArray alloc] init];
    int i = 0;
	for (NSString *song in fileArray)
	{
		MDAudioFile *audioFile = [[MDAudioFile alloc] initWithPath:[NSURL fileURLWithPath:song]];
        audioFile.songTitle = [songTitles objectAtIndex:i];
        i++;

		[songs addObject:audioFile];

        [audioFile release];
	}

    //NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    // Add your filename to the directory to create your saved pdf location
    //NSString *downloadLocation = [documentDirectory stringByAppendingPathComponent:@"Products"];

	MDAudioPlayerController *audioPlayer = [[MDAudioPlayerController alloc] initWithSoundFiles:songs atPath:downloadLocation andSelectedIndex:productIndex];
	[self.navigationController presentModalViewController:audioPlayer animated:YES];
	[audioPlayer release];

    [songs release];
Per abilitare la modalità di background playing del nostro player occorre eseguire i seguenti passi:
  1. abilitare gli UIBackgroundModes nel file Info.plist del nostro progetto: inserire la voce “Required background modes” e associargli il valore “App plays audio” 
  2. nel nostro MDAudioPlayerController occorre inserire le seguenti funzioni, per intercettare le azioni dei controlli quando l’app è in background:
    -(BOOL) canBecomeFirstResponder{
        return YES;
    }
    
    -(void) remoteControlReceivedWithEvent:(UIEvent *)event{
    
        if (event.type == UIEventTypeRemoteControl) {
    
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlTogglePlayPause:
                [self play];
                break;
    
            case UIEventSubtypeRemoteControlNextTrack:
                [self next];
                break;
    
            case UIEventSubtypeRemoteControlPreviousTrack:
                [self previous];
                break;
    
            default:
                break;
        }
    
        }
    
    }

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *


sei × = 42