How to play the music continously which are stored in NSArray? - ios

In tableview footer i have a play button to play the music, as the music starts tableview scrolls automatically according to the audio played but if the user tap on particular cell, the content in the particular cell is playing and stops but i want to continue the music from where the use taps to the end. Below is the code. Please help.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int ref =(int) indexPath.row;
NSString *filePath = [soundArray objectAtIndex:ref];
NSString *Path = [[NSBundle mainBundle] pathForResource:filePath ofType:#"mp3"];
NSURL *musicFile = [NSURL fileURLWithPath:Path];
myAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:nil];
if (playing == YES) {
[playButton setBackgroundImage:[UIImage imageNamed:#"pause_White.png"] forState:UIControlStateNormal];
[myAudioPlayer play];
// playing = NO;
}
if (playing == NO) {
// [playButton setBackgroundImage:[UIImage imageNamed:#"play_white.png"] forState:UIControlStateNormal];
}
}
SoundArray contains trimmed music. How to continue the song ? Help much appreciated.

Here is my code as per my concept. You may have to manage it more as its just a sample
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
isMainMusic = NO;
// Construct URL to sound file
NSString *path = [NSString stringWithFormat:#"%#/1.mp3", [[NSBundle mainBundle] resourcePath]];
mainMusicUrl = [NSURL fileURLWithPath:path];
NSString *path1 = [NSString stringWithFormat:#"%#/2.mp3", [[NSBundle mainBundle] resourcePath]];
secondMusicUrl = [NSURL fileURLWithPath:path1];
}
-(void)playSecondaryMusic
{
if ([playMainMusic isPlaying]) {
[playMainMusic pause];
}
if (playSecondaryMusic) {
playSecondaryMusic.delegate = nil;
playSecondaryMusic = nil;
}
isMainMusic = NO;
playSecondaryMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:secondMusicUrl error:nil];
playSecondaryMusic.delegate = self;
[playSecondaryMusic play];
}
- (IBAction)playMainMusic:(UIButton *)sender {
if (playMainMusic) {
playMainMusic.delegate = nil;
playMainMusic = nil;
}
isMainMusic = YES;
playMainMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:mainMusicUrl error:nil];
playMainMusic.delegate = self;
[playMainMusic play];
}
//Delegate method
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
if (isMainMusic)
{
NSLog(#"Main music");
}
else
{
NSLog(#"Second music");
[playMainMusic play];
isMainMusic = YES;
}
}

Related

Memory Management - Loading Sound Effects - Cocoa Xcode

So it has been brought to my attention that I should optimise the way I load and use sounds in a small game that I am developing for iOS.
I load a "boing" sound and play it every time I tap the screen, making the sprite jump (like mario).
I want to be able to play the sound every time, even if the sound is already playing from the previous jump...
Below are the 2 ways I'm currently using:
1st implementation:
//load the music from file
-(void)LoadMusic{
jumpSound = [[NSBundle mainBundle] pathForResource:#"boing" ofType:#"mp3"];
}
//call in viewDidLoad
- (void)viewDidLoad{
[self LoadMusic];
...
[super viewDidLoad];
}
//play sound when called
-(void)playSound{
jumpAffect = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
[jumpAffect play];
}
//tap/touch to jump (& play sound)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event{
[self playSound];
jumpUp = 16;
}
2nd implementation:, this is similar except I load the same file 5 times, and loop through to the next one (so the same sound affect can be called even if it's already in previous session).
int soundStage = 1;
//load the music from file
-(void)LoadMusic{
jumpSound = [[NSBundle mainBundle] pathForResource:#"Boing" ofType:#"mp3"];
jumpAffect = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect.delegate = self;
jumpAffect2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect2.delegate = self;
jumpAffect3 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect3.delegate = self;
jumpAffect4 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect4.delegate = self;
jumpAffect5 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
jumpAffect5.delegate = self;
}
//call in viewDidLoad
- (void)viewDidLoad{
[self LoadMusic];
...
[super viewDidLoad];
}
//tap/touch to jump (& play sound)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event{
jumpUp = 16;
if(soundStage == 1){
[jumpAffect play];
soundStage = 2;
}
else if(soundStage == 2){
[jumpAffect2 play];
soundStage = 3;
}
else if(soundStage == 3){
[jumpAffect3 play];
soundStage = 4;
}
else if(soundStage == 4){
[jumpAffect4 play];
soundStage = 5;
}
else if(soundStage == 5){
[jumpAffect5 play];
soundStage = 1;
}
I'm wondering which why is the better way? I'm hoping to avoid memory leaks and just optimise it by being able to continuously have the same sound play when the screen is tapped. Thanks.
AVAudioPlayer plays multiple sounds simultaneously, one sound per audio player. So better is to load/play your quick sound inside the touchesBegan method without keeping the reference:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
NSString *jumpSound = [[NSBundle mainBundle] pathForResource:#"boing" ofType:#"mp3"];
AVAudioPlayer *jumpAffect = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:jumpSound] error:NULL];
[jumpAffect prepareToPlay];
[jumpAffect play];
jumpUp = 16;
}

iOS mutable json

i am trying to do a radio app, i want to get the album art from itunes, am about to finish but i need some help here, i dont know how to make my json request take my metadata variables everytime the song change, here is my code in .m
#import "EDViewController.h"
#define STREAM_URL #"http://4893.live.streamtheworld.com:80/ROCK_FMAAC_SC"
#interface EDViewController ()
#end
#implementation EDViewController
- (void)viewDidLoad {
radio = [[Radio alloc] init:#""];
[radio connect:STREAM_URL withDelegate:self withGain:(1.0)];
playing = YES;
[super viewDidLoad];;
NSMutableString *urlString = [NSMutableString stringWithFormat:#"https://itunes.apple.com/search?term"];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *artwork = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSMutableArray *results = [artwork objectForKey:#"results"];
NSDictionary *album = [results objectAtIndex:0];
NSString *artalbum = [album objectForKey:#"artworkUrl100"];
NSURL *urlOne = [NSURL URLWithString:artalbum];
NSData *newData = [NSData dataWithContentsOfURL:urlOne];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(0, 69, 320, 325))];
[imageView setImage:[UIImage imageWithData:newData]];
[self.view addSubview:imageView];
UIImageView *imageView2 = [[UIImageView alloc] initWithFrame:(CGRectMake(95, 167, 130, 130))];
[imageView2 setImage:[UIImage imageWithData:newData]];
[self.view addSubview:imageView2];
}
- (IBAction)play {
[radio resume];
}
- (IBAction)stop {
[radio updatePlay:NO];
}
- (IBAction)pause {
[radio pause];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark -
- (void)uodateBuffering:(BOOL)value {
NSLog(#"delegate update buffering %d", value);
}
- (void)interruptRadio {
NSLog(#"delegate radio interrupted");
}
- (void)resumeInterruptRadio {
NSLog(#"delegate resume interrupted Radio");
}
- (void)networkChanged {
NSLog(#"delegate network changed");
}
- (void)connectProblem {
NSLog(#"delegate connection problem");
}
- (void)audioUnplugged {
NSLog(#"delegate audio unplugged");
}
- (void)metaTitleUpdated:(NSString *)title {
NSLog(#"delegate title updated to %#", title);
NSArray *chunks = [title componentsSeparatedByString:#";"];
if ([chunks count]) {
NSArray *streamTitle = [[chunks objectAtIndex:0] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"'-"]];
if ([streamTitle count] > 1) {
titleLabel.text = [streamTitle objectAtIndex:1];
}
NSArray *streamArtist = [[chunks objectAtIndex:0] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"'-"]];
if ([streamArtist count] > 1) {
test100.text = [streamArtist objectAtIndex:2];
}
}
}
#end
As you can see my metadata info is at the end of my code and my json request its almost at the top.`

How can I use this code to play more sounds?

//Action to play Audio//
-(IBAction)playAudio:(id)sender {
[self.loopPlayer play];
}
//Action to stop Audio//
-(IBAction)stopAudio:(id)sender {
if (self.loopPlayer.isPlaying) {
[self.loopPlayer stop];
self.loopPlayer.currentTime = 0;
self.loopPlayer.numberOfLoops = -1;
[self.loopPlayer prepareToPlay];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//Code that gets audio file "trap synth"//
NSURL* audioFileURL = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];
}
This is the code i'm using with one button to play the sound when the button is tapped and stop the sound when the button is released. How would I go about adding more sounds to more buttons? I want to have more buttons that play and stop different sounds just like this.
property (nonatomic, strong) AVAudioPlayer *loopPlayer;
This code is also in my ViewController.h file
Ok although the answer provided by Miro is on the write track the code example given has issues.
Should be this in viewDidLoad -
- (void)viewDidLoad {
[super viewDidLoad];
NSURL* audioFileURL1 = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL1 error:nil];
NSURL* audioFileURL2 = [[NSBundle mainBundle] URLForResource:#"other_audio_file" withExtension:#"wav"];
self.loopPlayer2 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL2 error:nil];
}
also stopAudio: method should be this
-(IBAction)stopAudio:(id)sender {
if (self.loopPlayer1.isPlaying && (sender.tag == 1)) {
[self.loopPlayer1 stop];
self.loopPlayer1.currentTime = 0;
self.loopPlayer1.numberOfLoops = -1;
[self.loopPlayer1 prepareToPlay];
}
if (self.loopPlayer2.isPlaying && (sender.tag == 2)) {
[self.loopPlayer2 stop];
self.loopPlayer2.currentTime = 0;
self.loopPlayer2.numberOfLoops = -1;
[self.loopPlayer2 prepareToPlay];
}
}
And finally for playAudio:
-(IBAction)playAudio:(id)sender {
if([sender tag] == 1){
[self.loopPlayer1 play];
}
if([sender tag] == 2){
[self.loopPlayer2 play];
}
}
If you want to play different sounds at the same time you should look into creating separate AVAudioPlayers - if you create a different one for each sound, then you can easily control (play/stop) each of them separately with a specific button.
On the simplest level, you could do something like this, which allows you to use the same button handlers for all your audio. The playAudio checks the tag of the Play button you press (be sure to set the tag value in IB, to 1,2,etc). There really only need be one Stop button.
You could enhance this in many ways, like attempting to reuse the AVAudioPlayer somehow, and loading the audio on the fly instead of all at the beginning. Or storing your audio file info in an array, creating an array of AVAudioPlayers for management, etc. But this is a start.
-(IBAction)playAudio:(id)sender {
// first, stop any already playing audio
[self stopAudio:sender];
if([sender tag] == 1){
[self.loopPlayer1 play];
} else if([sender tag] == 2){
[self.loopPlayer2 play];
}
}
-(IBAction)stopAudio:(id)sender {
if (self.loopPlayer1.isPlaying) {
[self.loopPlayer1 stop];
self.loopPlayer1.currentTime = 0;
self.loopPlayer1.numberOfLoops = -1;
[self.loopPlayer1 prepareToPlay];
} else if (self.loopPlayer2.isPlaying) {
[self.loopPlayer2 stop];
self.loopPlayer2.currentTime = 0;
self.loopPlayer2.numberOfLoops = -1;
[self.loopPlayer2 prepareToPlay];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
NSURL* audioFileURL1 = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];
NSURL* audioFileURL2 = [[NSBundle mainBundle] URLForResource:#"trapsynth" withExtension:#"wav"];
self.loopPlayer2 = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];
}
AND, in the .h file;
property (nonatomic, strong) AVAudioPlayer *loopPlayer1;
property (nonatomic, strong) AVAudioPlayer *loopPlayer2;

Play and Pause function

I have a soundboard app that plays sound and is supposed to play music and if you click the button then it pauses but it doesn't pause it only stops the music.
And once the music finishes the button stays selected.
My code is;
- (IBAction)aint:(id)sender {
UIButton *aint = (UIButton *)sender;
aint.selected = !aint.selected;
if(aint.selected)
{
// Play
NSString *path2 = [[NSBundle mainBundle] pathForResource:#"2" ofType:#"mp3"];
theAudio2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path2] error:NULL];
theAudio2.delegate = self;
theAudio2.numberOfLoops = 0;
[theAudio2 play];
[[NSUserDefaults standardUserDefaults] setObject:#"-" forKey:#"music"];
}
else
{
// Pause
[theAudio2 pause];
}
}
And I have theAudio2 and AVAudioPlayer declared.
Try this it will work as expected -
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path2 = [[NSBundle mainBundle] pathForResource:#"2" ofType:#"mp3"];
theAudio2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path2] error:NULL];
theAudio2.delegate = self;
theAudio2.numberOfLoops = 0;
}
- (IBAction)aint:(id)sender
{
UIButton *aint = (UIButton *)sender;
aint.selected = !aint.selected;
if(aint.selected)
{
// Play
[theAudio2 play];
[[NSUserDefaults standardUserDefaults] setObject:#"-" forKey:#"music"];
}
else
{
// Pause
[theAudio2 pause];
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
playerBtn.selected = NO;
}

My buttons play all the same sound?

Here is my MainViewController.m
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize audioPlayer;
#synthesize soundsArray;
-(void)prepareSounds
{
NSString *filepath= [[NSBundle mainBundle] pathForResource:#"Sounds" ofType:#"plist"];
self.soundsArray = [[NSArray alloc] initWithContentsOfFile:filepath];
}
- (IBAction)playSound:(id)sender {
UIButton *buttonPressed = (UIButton *)sender;
NSString *soundName = [soundsArray objectAtIndex:(buttonPressed.tag -1)];
NSString *path = [[NSBundle mainBundle] pathForResource:soundName ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *p = [[AVAudioPlayer alloc]
initWithContentsOfURL:file error:nil];
self.audioPlayer = p;
[self.audioPlayer play];
}
- (IBAction)playSound2:(id)sender {
UIButton *buttonPressed = (UIButton *)sender;
NSString *soundName = [soundsArray objectAtIndex:(buttonPressed.tag -2)];
NSString *path = [[NSBundle mainBundle] pathForResource:soundName ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *p = [[AVAudioPlayer alloc]
initWithContentsOfURL:file error:nil];
self.audioPlayer = p;
[self.audioPlayer play];
}
- (IBAction)playSound3:(id)sender {
UIButton *buttonPressed = (UIButton *)sender;
NSString *soundName = [soundsArray objectAtIndex:(buttonPressed.tag -3)];
NSString *path = [[NSBundle mainBundle] pathForResource:soundName ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *p = [[AVAudioPlayer alloc]
initWithContentsOfURL:file error:nil];
self.audioPlayer = p;
[self.audioPlayer play];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Flipside View
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)showInfo:(id)sender
{
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideViewController" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:controller animated:YES completion:nil];
}
#end
My MainViewController.h
#import <UIKit/UIKit.h>
#import "FlipsideViewController.h"
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {
AVAudioPlayer *audioPlayer;
NSArray *soundsArray;
}
#property(nonatomic, retain) AVAudioPlayer *audioPlayer;
#property(nonatomic, retain) NSArray *soundsArray;
-(void)prepareSounds;
- (IBAction)playSound:(id)sender;
- (IBAction)playSound2:(id)sender;
- (IBAction)playSound3:(id)sender;
#end
In the 'Supporting Files' folder I have an array of strings with the name of the sound files I want to play, and in the 'Supporting Files' folder I have a folder named 'Sounds', which contains the sound files.
All of my buttons play the same sound. Can someone please provide some insight. Thanks!
I think the problem may be in this line playSound:
NSString *soundName = [soundsArray objectAtIndex:(buttonPressed.tag -1)]
which is repeated in playSound2 and playSound3 with "buttonPressed.tag -2" and "buttonPressed.tag -3".
If your buttonPressed.tags are set to 1, 2, and 3, then each time "buttonPressed.tag -X" is likely evaluating to 0, and playing the sound of the first file in the array.
You are repeating the code to do the same task.
Add all your buttons IBAction to a single method (say it as playSound)
Implement the method like:
- (IBAction)playSound:(UIButton *)sender
{
NSString *soundName = [soundsArray objectAtIndex:(sender.tag -1)];
NSString *path = [[NSBundle mainBundle] pathForResource:soundName ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *p = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:nil];
self.audioPlayer = p;
[self.audioPlayer play];
}
There is no need of writing same code for each individual button.
SOLVED: prepareSounds() was never called. Here is the working code:
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize audioPlayer;
#synthesize soundsArray;
-(void)prepareSounds
{
NSString *filepath= [[NSBundle mainBundle] pathForResource:#"Sound" ofType:#"plist"];
self.soundsArray = [[NSArray alloc] initWithContentsOfFile:filepath];
}
- (void)stopAudio
{
if (audioPlayer!= nil) {
[audioPlayer stop];
//do some task for changing the Image i.e setting the default image
}
}
- (IBAction)playSound:(UIButton *)sender
{
UIButton *btn = (UIButton*)sender;
NSString *soundName = [soundsArray objectAtIndex:(btn.tag - 1)];
NSString *path = [[NSBundle mainBundle] pathForResource:soundName ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *p = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:nil];
self.audioPlayer = p;
if([audioPlayer isPlaying])
{
[self stopAudio];
}
[self.audioPlayer play];
}
- (void)viewDidLoad
{
[self prepareSounds];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Flipside View
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)showInfo:(id)sender
{
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideViewController" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:controller animated:YES completion:nil];
}
#end

Resources