AVAudioPlayer noises, irreptions - objective-c

I have seen a lot questions about AVAudioPlayer, but I haven't found answer to my question.
First I describe my problem
I have one class
#interface Ex_1: UIView {
AVAudioPlayer *player;
}
#property(retain, nonatomic) AVAudioPlayer *player;
-(void) playSongByParameters;
#end
#implementation Ex_1
#synthesize player;
-(void) playSongByParameters {
//here i download mp3 from url and then i get NSData *data
//then
NSError *err;
if (player) {
[player stop];
[player release];
}
player = [[AVAudioPlayer alloc] initWithData : data : &err];
if (!err) {
[player prepareToPlay];
[player play];
}
}
#end
So in main class I create object of Ex_1 class and sometimes I call playSongByParameters method. Sometimes song plays completely and it's ok, but sometimes same song is playing during 30( maybe less, maybe more ) seconds and then it slows and then noises appear and then it interrupted.
Onetime I stop program with breakpoint(when its problem appears) and then i continue running program and song plays completely without bugs, noises.
Can you tell me what is problem ? (I use non arc)

Related

Custom Segue doesn't Play Sound

I made a custom segue and I want it to play sound when changing views. Unfortunately, it doesn't. Method playSound works fine when I use it in View Controller. But i don't want to export it in every View Controller i need. How can i improve my code and why segue doesn't want to play sound?
#import "CustomSegue.h"
#import AVFoundation;
#interface CustomSegue()
#property (nonatomic, strong) AVAudioPlayer *player;
#end
#implementation CustomSegue
#synthesize player;
- (void) perform
{
UIViewController *src = (UIViewController *) self.sourceViewController;
UIViewController *dst = (UIViewController *) self.destinationViewController;
[UIView transitionWithView:src.navigationController.view duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[src.navigationController pushViewController:dst animated:NO];
}
completion:NULL];
[self playSound];
}
- (void) playSound
{
int r = arc4random_uniform(7) + 1;
NSURL *soundUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%i", r] ofType:#"caf"]];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
[self.player prepareToPlay];
[self.player play];
}
I bet your custom segue is released (by ARC) before the sound has a chance to play, and your audio player is released with it. The Life Cycle of a Segue is such that it is deallocated immediately after -performis executed.
What you need to do is keep the audio player around for a longer time. One way to do this is to add a (strong) reference to your AVAudioPlayer from an object that is not going to be deallocated for at least as long as the sound will play. For example, place the player in your app delegate, which sticks around as long as the app is running.
Try moving the player property and the -playsound method into your app delegate. The property should look something like:
#property (strong, nonatomic) AVAudioPlayer *player;
Then, in this segue, in place of [self playsound]; make a call to the app delegate with something like:
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate playsound];

Unrecognized Selector in AVplayer setNumberOfLoops Method

When calling the numberOfLoops method like so:
[_player setNumberOfLoops:-1];
I get the following error:
-[AVPlayer setNumberOfLoops:]: unrecognized selector sent to instance 0x7d52d30
How can this be fixed?
Code:
Header:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController : UIViewController {
}
#property (strong, nonatomic) AVAudioPlayer *player;
- (IBAction)playMusic:(id)sender;
#end
Implementation:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#interface ViewController ()
#end
#implementation ViewController
- (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.
}
- (IBAction)playMusic:(id)sender {
_player = [AVPlayer playerWithURL:[NSURL URLWithString:#"http://urlpath.wav"]];
[_player setNumberOfLoops:-1];
[_player prepareToPlay];
[_player play];
}
#end
Thank you for your time,
Yoni201.
You've created an instance of AVPlayer, not an instance of AVAudioPlayer. It looks like you want to be creating an AVAudioPlayer instead (as is indicated by your choice of that class for the actual player property on your class. AVAudioPlayer actually has the numberOfLoops property, while AVPlayer does not. For more information, see the documentation for AVAudioPlayer and AVPlayer.
AVPlayer doesn't have a numberOfLoops property. That is a property of `AVAudioPlayer. Don't ignore compiler warnings when you build your app.
Also, you defined _player to be an AVAudioPlayer but you alloc/init AVPlayer.
Change your code to:
NSError *error = nil;
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://urlpath.wav"] error:&error];
if (player) {
[player setNumberOfLoops:-1];
[player prepareToPlay];
[player play];
self.player = player;
} else {
NSLog(#"Error create audio player: %#", error);
}
I think that the method you are calling doesn't exist (acceosing to your error).
Try: _player.numberOfLoops=-1

AVAudioPlayer not playing iPad2

New to IOS dev, I'm testing AVAudioplayer to play sound on iPad2 (Xcode 4.2 project, ARC/storyboard enabled). Sound plays ok in simulator and no error. No error on device either but no sound.
Been browsing this fine resource temple, but nothing I've tried based on feedback here has produced anything but deafening iPad silence. Could someone help? My .h:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController : UIViewController
<AVAudioPlayerDelegate>
{
AVAudioPlayer *audioPlayer;
UISlider *volumeControl;
UILabel *timerLabel;
NSTimer *playbackTimer;
}
#property (nonatomic, retain) IBOutlet UISlider *volumeControl;
#property (nonatomic, retain) IBOutlet UILabel *timerLabel;
#property (nonatomic, retain) NSTimer *playbackTimer;
#property (nonatomic, strong) AVAudioPlayer *audioPlayer;
-(IBAction) playAudio;
-(IBAction) stopAudio;
-(IBAction) adjustVolume;
#end
My .m:
#import "ViewController.h"
#implementation ViewController
#synthesize volumeControl, timerLabel, playbackTimer, 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;
}
-(void)audioPlayerDidFinishPlaying:
(AVAudioPlayer *)player successfully:(BOOL)flag
{
}
-(void)audioPlayerDecodeErrorDidOccur:
(AVAudioPlayer *)player error:(NSError *)error
{
}
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
}
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
}
my viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"song"
ofType:#"mp3"]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
}
[super viewDidLoad];
}
Make sure that the file is indeed an mp3 format. Make sure that you are copying the file into the bundle, and not playing off of local path on your desktop. Check the device volume. Check the return BOOL from the play call. all of these are possible explanations.
Is it not playing sounds at all? No sounds even with headphones plugged in? If it's just no sound through the built-in speaker, but sounds through headphones, make sure that your device ring/sounds volume isn't muted. Check the toggle switch on the side (if you have that set to mute vs orientation lock). The bell shouldn't be crossed out. Just because you press the volume up button doesn't mean it's unmuted from the speaker. Have you tested YouTube videos or music files to ensure your iPad isn't having hardware issues?

Why is this object release not OK, should i be releasing it?

You'll have to forgive me because i'm still fairly new to Obj-C but i'm quite confused..
I have this little sound board app with 12 buttons.. each calling the same IBAction..
When the user taps the button i'm calling alloc init on the player variable (which is declared in the interface part of the class)
This works all fine and dandy:
#pragma mark - IBActions
-(IBAction)userDidTapButton:(id)sender {
[player stop];
NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"clip" ofType:#"mp3"]];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];
[player setNumberOfLoops:-1];
[player play];
}
#pragma mark - Cleanup
- (void)dealloc {
[player release];
[super dealloc];
}
However this feels like when i'm calling alloc init repeatedly i'm leaving memory dangling (because i'm assigning the player pointer to a new variable without releasing the old one..)
To remedy this I tried adding this at the top of the IBAction:
-(IBAction)userDidTapButton:(id)sender {
[player stop];
[player release];
... etc ...
This works the first time i click the button (which seems strange to me as it's effectively a null pointer because it hasn't been allocated and initialised (right?)) but when i tap the button again it throws a EXC_BAD_ACCESS signal..
Why?
I allocated the memory should't I be freeing it too?
How am i supposed to free it?
Thanks in adavance!
So I'll walk you through how I would do it and why.
In your .h file declare the player ivar with a property like this
// .h
#interface MyClass : UIViewController
#property (nonatomic, retain) AVAudioPlayer *audioPlayer;
// method signatures
#end
I named it audioPlayer just to be more explicit (this is personal preference).
In your implementation file you need to synthesize this ivar like this
// .m
#implementation MyClass
#synthesize audioPlayer = _audioPlayer;
// Do some stuff
#end
This will create the backing ivar and the getter and setter with the signatures - (void)setAudioPlayer:(AVAudioPlayer *)audioPlayer and - (AVAudioPlayer *)audioPlayer; but in the background they will be manipulating the ivar _audioPlayer.
You mentioned in a reply that you come from Ruby this can be likened to something like this attr_accessor :audio_player but in Objective-C it creates setters and getters than can deal with memory management depending on whether you pass in assign/retain/copy into the #property line.
This is how Apple does it in most of their examples and it means that it is clearer when you are accessing the ivar directly or going through a getter/setter.
I would now change your -(IBAction)userDidTapButton:(id)sender to look like this
-(IBAction)userDidTapButton:(id)sender
{
[self.audioPlayer stop];
NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"clip" ofType:#"mp3"]];
AVAudioPlayer *tmpPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];;
self.audioPlayer = tmpPlayer;
[tmpPlayer release]; tmpPlayer = nil;
[self.audioPlayer setNumberOfLoops:-1];
[self.audioPlayer play];
}
I have used the getters/setters anytime I have interacted with the audioPlayer ivar. This means that the memory management is taken care of each time I set the ivar (e.g. it releases the old player and retains the new). The reason this is using the getters/setters is because of the self.audioPlayer which will be compiled to the appropriate call like this:
self.audioPlayer; // compiled to -> [self audioPlayer];
self.audioPlayer = tmpPlayer; // compiled to -> [self setAudioPlayer:tmpPlayer];
Now to tidy up and make the - (void)dealloc; method correct we should use the ivar directly without going through the getter/setters so I have to use the _audioPlayer ivar that we synthesized like this:
#pragma mark - Cleanup
- (void)dealloc
{
[_audioPlayer release];
[super dealloc];
}
I sometimes get these weird problems too. A good habit to get into for Objective-C code is the same pattern with all allocated objects: call alloc init, do something (including retain it), then release. I find if you do that all in the same method things go predictably.
So, in your case, try the following:
-(IBAction)userDidTapButton:(id)sender {
[myPlayer stop];
[myPlayer release];
NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"clip" ofType:#"mp3"]];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];
[player setNumberOfLoops:-1];
[player play];
myPlayer = [player retain];
[player release];
}
where myPlayer is an instance variable in your class.
You should probable release the previous player before you create a new one, or just reuse the one you have previously created. So after [player stop]; add [player release]; player = nil; the = nil; is so you can then safely send release in you dealloc methods. You should also probable add a [player stop]; before you [player release]; in you dealloc method. You may also want to keep a AVAudioPlayer instance for each button if there are not too many.
However this feels like when i'm calling alloc init repeatedly i'm leaving memory danglin
Yes, you are. You should release the old player before allocing the new one.
-(IBAction)userDidTapButton:(id)sender {
[player stop];
NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"clip" ofType:#"mp3"]];
[player release]; // <<<=== this needs to be here
player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];
[player setNumberOfLoops:-1];
[player play];
}
However, it is better to create a property for the player to take care of all of this:
#interface MyClass : WhateverSuperClass
{
#private
// other ivars
AVAudioPlayer* player
}
#property (retain) AVAudioPlayer* player;
// other methods
#end
#implementation MyClass
#synthesize player;
// other stuff
-(IBAction)userDidTapButton:(id)sender {
[[self player] stop];
NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"clip" ofType:#"mp3"]];
[self setPlayer: [[[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil] autorelease]];
[[self player] setNumberOfLoops:-1];
[[self player] play];
}
- (void)dealloc
{
[player release];
[super dealloc];
}

cocoa playing an mp3

Is there an easy way to load, play and control an mp3 file from cocoa? Tried googling it, but, as all things apple, i get messy results and have no idea where to start. As i understand, there's and NSSound, but it has lots of limitations and then there's CoreAudio, but it's very difficult. So can someone point me in a right direction with this? Thanks.
Use AVFoundation! According to apple, it has been integrated with Mac OS X Lion (I think), so.. Here is how to play mp3 painlessly:
1- link the AVFoundation Framework.
2- import it wherever you want to play your awesome mp3
#import <AVFoundation/AVFoundation.h>
3- Add an audioPlayer instance variable to play the sound (That's how I like to do it, at least)
#interface WCMainWindow : ... {
...
AVAudioPlayer* audioPlayer;
}
4- In you init, make sure you initialize your audioPlayer:
NSString* path = [[NSBundle mainBundle] pathForResource:#"myFile" ofType:#"mp3"];
NSURL* file = [NSURL fileURLWithPath:path];
// thanks #gebirgsbaerbel
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:nil];
[audioPlayer prepareToPlay];
5- Play your awesome Mp3!!
if ([audioPlayer isPlaying]) {
[audioPlayer pause];
} else {
[audioPlayer play];
}
Finally, credit goes to this guy.
I've written a framework in C++ that might help: http://github.com/sbooth/SFBAudioEngine
It supports multiple audio formats and has a fairly benign API and comes with a Cocoa sample.
If you're not interested in third-party frameworks, your bet would probably be to use an AudioQueue to take care of the playback. To do this, you'd probably use AudioFile to decode the MP3 and AudioQueue for the playback. Apple has an example at http://developer.apple.com/mac/library/samplecode/AudioQueueTools/Introduction/Intro.html
Use NSSound.
You didn't specify what you mean by “lots of limitations”, so I don't know why this won't work for you. Note that it has a lot fewer limitations since Leopard; you can now play to any device, for example.
#import <Cocoa/Cocoa.h>
#import <QTKit/QTKit.h>
#interface SoundPlayer : NSObject {
NSSound *sound;
IBOutlet NSWindow *window;
IBOutlet NSSlider *progress;
BOOL loop;
}
- (IBAction)open: (id)sender;
- (IBAction)setLoop: (id)sender;
- (IBAction)play: (id)sender;
- (IBAction)stop: (id)sender;
- (IBAction)takeCurrentTimeFrom: (id)sender;
#end
#import "SoundPlayer.h"
#implementation SoundPlayer
- (IBAction)open: (id)sender
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel runModalForTypes: [NSArray arrayWithObjects: #"aiff", #"mp3", #"m4a", nil]];
[sound release];
sound = [[QTMovie movieWithFile: [openPanel filename]
error: NULL] retain];
[sound setAttribute: [NSNumber numberWithBool: loop]
forKey: QTMovieLoopsAttribute];
[window setTitle: [[openPanel filename] lastPathComponent]];
}
- (IBAction)setLoop: (id)sender
{
loop = ([sender state] == NSOnState);
[sound setAttribute: [NSNumber numberWithBool: loop]
forKey: QTMovieLoopsAttribute];
}
- (IBAction)play: (id)sender
{
NSTimeInterval duration;
QTGetTimeInterval([sound duration], &duration);
[progress setMaxValue: duration];
[NSTimer scheduledTimerWithTimeInterval: 0.1
target: self
selector: #selector(updateIndicator:)
userInfo: sound
repeats: YES];
[sound play];
}
- (void)updateIndicator: (NSTimer*)aTimer
{
QTMovie *playingSound = [aTimer userInfo];
if (!(sound == playingSound && ([sound rate] != 0)))
{
[aTimer invalidate];
return;
}
NSTimeInterval currentTime;
QTGetTimeInterval([sound currentTime], &currentTime);
[progress setDoubleValue: currentTime];
}
- (IBAction)stop: (id)sender
{
[sound stop];
}
- (IBAction)takeCurrentTimeFrom: (id)sender
{
[sound setCurrentTime: QTMakeTimeWithTimeInterval([sender doubleValue])];
}
#end
Besides NSSound, you could consider QTMovieView. It sounds odd, but you can have a hidden QTMovieView in your window and use it to play an MP3.
Added: QTMovieView is deprecated as of OS 10.9. So unless you need to support OS versions before 10.7 (when AVFoundation came to the Mac), you should probably not use this.
import Cocoa
import AVFoundation
class ViewController: NSViewController {
var audio = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
let play = Bundle.main.path(forResource: "A" , ofType: "mb3")
do{
audio = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: play!))
}
catch{
print(error)
}
}
#IBAction func button(_ sender: Any)
{
audio.play()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}