Hello Okay right I have a view called 'RecordViewController' this has the voice recorder function so once the user presses 'record', it records their voice, I have also created a 'back' button. But once that back button is tapped the voice recording also stops. I want it so once the user goes back, its still recording their voice. Here is the code I used:
.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
#interface RecordViewController : UIViewController <AVAudioRecorderDelegate> {
IBOutlet UIButton * btnStart;
IBOutlet UIButton * btnPlay;
IBOutlet UIActivityIndicatorView * actSpinner;
BOOL toggle;
NSURL * recordedTmpFile;
AVAudioRecorder * recorder;
NSError * error;
NSTimer *theTimer;
IBOutlet UILabel *seconds;
int mainInt;
NSString *timeRemainingString;
}
#property (nonatomic,retain)IBOutlet UIActivityIndicatorView * actSpinner;
#property (nonatomic,retain)IBOutlet UIButton * btnStart;
#property (nonatomic,retain)IBOutlet UIButton * btnPlay;
- (IBAction) start_button_pressed;
- (IBAction) play_button_pressed;
- (IBAction)goBack:(id)sender;
- (void)countUp;
#end
.m
#import "RecordViewController.h"
#implementation RecordViewController
#synthesize actSpinner, btnStart, btnPlay;
- (void)countUp {
mainInt += 1;
seconds.text = [NSString stringWithFormat:#"%02d", mainInt];
}
- (IBAction)goBack:(id)sender; {
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidLoad {
[super viewDidLoad];
//Start the toggle in true mode.
toggle = YES;
btnPlay.hidden = YES;
//Instanciate an instance of the AVAudioSession object.
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
//Setup the audioSession for playback and record.
//We could just use record and then switch it to playback leter, but
//since we are going to do both lets set it up once.
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
error: &error];
//Activate the session
[audioSession setActive:YES error: &error];
}
- (IBAction)start_button_pressed {
if(toggle) {
toggle = NO;
[actSpinner startAnimating];
[btnStart setImage:[UIImage imageNamed:#"stoprecording.png"]
forState:UIControlStateNormal];
mainInt = 0;
theTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countUp) userInfo:nil repeats:YES];
btnPlay.enabled = toggle;
btnPlay.hidden = !toggle;
//Begin the recording session.
//Error handling removed. Please add to your own code.
//Setup the dictionary object with all the recording settings that this
//Recording sessoin will use
//Its not clear to me which of these are required and which are the bare minimum.
//This is a good resource: http://www.totodotnet.net/tag/avaudiorecorder/
NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
//Now that we have our settings we are going to instanciate an instance of our recorder instance.
//Generate a temp file for use by the recording.
//This sample was one I found online and seems to be a good choice for making a tmp file that
//will not overwrite an existing one.
NSString *fileName = [NSString stringWithFormat:#"%.0f.caf", [NSDate timeIntervalSinceReferenceDate] * 1000.0];
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
recordedTmpFile = [NSURL fileURLWithPath:path];
NSLog(#"Using File called: %#",recordedTmpFile);
//Setup the recorder to use this file and record to it.
recorder = [[AVAudioRecorder alloc] initWithURL:recordedTmpFile settings:recordSetting error:&error];
//Use the recorder to start the recording.
//Im not sure why we set the delegate to self yet.
//Found this in antother example, but Im fuzzy on this still.
[recorder setDelegate:self];
//We call this to start the recording process and initialize
//the subsstems so that when we actually say "record" it starts right away.
[recorder prepareToRecord];
//Start the actual Recording
[recorder record];
//There is an optional method for doing the recording for a limited time see
//[recorder recordForDuration:(NSTimeInterval) 10]
} else {
toggle = YES;
[actSpinner stopAnimating];
[btnStart setImage:[UIImage imageNamed:#"recordrecord.png"] forState:UIControlStateNormal];
btnPlay.enabled = toggle;
btnPlay.hidden = !toggle;
[theTimer invalidate];
NSLog(#"Using File called: %#",recordedTmpFile);
//Stop the recorder.
[recorder stop];
}
}
- (IBAction)play_button_pressed{
//The play button was pressed...
//Setup the AVAudioPlayer to play the file that we just recorded.
AVAudioPlayer * avPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:recordedTmpFile error:&error];
[avPlayer prepareToPlay];
[avPlayer play];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
//Clean up the temp file.
NSFileManager * fm = [NSFileManager defaultManager];
[fm removeItemAtPath:[recordedTmpFile path] error:&error];
//Call the dealloc on the remaining objects.
[recorder dealloc];
recorder = nil;
recordedTmpFile = nil;
}
#end
As you have structured your code now, the audio recording code is completely dependent on the presence of the user interface. As soon as your view controller gets deallocated, the audio recorder gets destroyed, too.
You have to decouple the audio recording code from the user interface code. Create a separate class that contains all properties and functionality that is related to recording. Then create an instance of that class outside your view controller (e.g. in your application delegate) and only pass this object to your view controller. The important thing is that the recorder object is totally unaffected by the presence or absence of the view controller.
Related
I'm working on application which has record audio and play that recorded file which is store in document directory.
Here is my code:
Record button method.
(IBAction)stopButtonPressed:(id)sender{
UIButton *button = (UIButton *)sender;
if (button.selected) { // Play recorded file.
self.stopButton.selected = NO;
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioRecorder.url error:&error];
self.audioPlayer.delegate = self;
self.audioPlayer.volume = 1.0;
[self.audioPlayer prepareToPlay];
[self.audioPlayer play];
}
else{ // Stop recording.
self.stopButton.selected = YES;
if (recordTimer) {
[recordTimer invalidate];
}
[self.audioPlayer stop];
self.recordButton.selected = NO;
[audioRecorder stop];
self.readyLabel.text = #"READY";
[self insertNewRecording];
}
}
I take it you're using ARC, in which case you'll need to retain the AVAudioPlayer as it sets to nil automatically. In your header file enter the following`
#property (nonatomic, strong) AVAudioPlayer *audioPlayer;
Hope this works for you,
Cheers Jim
um..I am not sure. But as a test, maybe you can try if you can play the file by specifying its name? E.g. you got a forest.mp3 to play and the key part of the code can be like this:
path = [[NSBundle mainBundle] pathForResource:#"forest" ofType:#"mp3"];
NSData *sampleData = [[NSData alloc] initWithContentsOfFile:path];
NSError *audioError = nil;
av = [[AVAudioPlayer alloc] initWithData:sampleData error:&audioError];
If everything went well, maybe it's something to do with audioRecorder.url in your code?
Hope this gives you some ideas.
I'm looking for the xCode to record system audio on the iPhone directly into an m4a file. Specifically, I'd like to record an audio file playing on the device and input from the mic at the same time. I understand there is the risk of picking up other audio events and would want to pause or stop my recording (for example, I receive a text and there is a chime.... Would want to stop the record)..
You can use AVAudioRecorder
Below code taken from techotopia.com
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface RecordViewController : UIViewController
<AVAudioRecorderDelegate, AVAudioPlayerDelegate>
#property (strong, nonatomic) AVAudioRecorder *audioRecorder;
#property (strong, nonatomic) AVAudioPlayer *audioPlayer;
#property (strong, nonatomic) IBOutlet UIButton *recordButton;
#property (strong, nonatomic) IBOutlet UIButton *playButton;
#property (strong, nonatomic) IBOutlet UIButton *stopButton;
- (IBAction)recordAudio:(id)sender;
- (IBAction)playAudio:(id)sender;
- (IBAction)stop:(id)sender;
#end
Creating the AVAudioRecorder Instance
- (void)viewDidLoad {
[super viewDidLoad];
_playButton.enabled = NO;
_stopButton.enabled = NO;
NSArray *dirPaths;
NSString *docsDir;
dirPaths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
NSString *soundFilePath = [docsDir
stringByAppendingPathComponent:#"sound.caf"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSDictionary *recordSettings = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:AVAudioQualityMin],
AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16],
AVEncoderBitRateKey,
[NSNumber numberWithInt: 2],
AVNumberOfChannelsKey,
[NSNumber numberWithFloat:44100.0],
AVSampleRateKey,
nil];
NSError *error = nil;
_audioRecorder = [[AVAudioRecorder alloc]
initWithURL:soundFileURL
settings:recordSettings
error:&error];
if (error)
{
NSLog(#"error: %#", [error localizedDescription]);
} else {
[_audioRecorder prepareToRecord];
}
}
Implementing the Action Methods
- (IBAction)recordAudio:(id)sender {
if (!_audioRecorder.recording)
{
_playButton.enabled = NO;
_stopButton.enabled = YES;
[_audioRecorder record];
}
}
- (IBAction)playAudio:(id)sender {
if (!_audioRecorder.recording)
{
_stopButton.enabled = YES;
_recordButton.enabled = NO;
NSError *error;
_audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:_audioRecorder.url
error:&error];
_audioPlayer.delegate = self;
if (error)
NSLog(#"Error: %#",
[error localizedDescription]);
else
[_audioPlayer play];
}
}
- (IBAction)stop:(id)sender {
_stopButton.enabled = NO;
_playButton.enabled = YES;
_recordButton.enabled = YES;
if (_audioRecorder.recording)
{
[_audioRecorder stop];
} else if (_audioPlayer.playing) {
[_audioPlayer stop];
}
}
Implementing the Delegate Methods
-(void)audioPlayerDidFinishPlaying:
(AVAudioPlayer *)player successfully:(BOOL)flag
{
_recordButton.enabled = YES;
_stopButton.enabled = NO;
}
-(void)audioPlayerDecodeErrorDidOccur:
(AVAudioPlayer *)player
error:(NSError *)error
{
NSLog(#"Decode Error occurred");
}
-(void)audioRecorderDidFinishRecording:
(AVAudioRecorder *)recorder
successfully:(BOOL)flag
{
}
-(void)audioRecorderEncodeErrorDidOccur:
(AVAudioRecorder *)recorder
error:(NSError *)error
{
NSLog(#"Encode Error occurred");
}
You can see the details here
I have never tried to record audio from both the mic and from streaming audio at the same time, however I noticed the previous answer was recording to a .caf file instead of a .m4a and that it did not set up the AVAudioSession, so I thought I would provide this link to a similar question I answered previously:
AVAudioRecorder record AAC/m4a
Note that setting up the AVAudioSession is what allows other applications (such as the phone app) to grab control of the mic and speaker and pause your app. In the code I linked to I use a Record session, you probably want to use the Playback and Record session to allow for both activities. Also, you can handle the notifications described in this doc from apple to handle what happens when your app is interrupted or if control is returned:
http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html
I know this is not a complete answer, but I hope it helps.
I am using following code: mY Sound is placed in myapp/Shared Resources/background_music.aiff
#synthesize window = _window;
- (void)dealloc
{
[backgroundSound release];
[_window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
sleep(3);
//this variable can be named differently
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/background_music.aiff",[[NSBundle mainBundle] resourcePath]]];
NSError *error;
backgroundSound = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
backgroundSound.numberOfLoops = -1;
//>> this will allow the file to //play an infinite number of times */
return YES;
}
#interface AppDelegate : UIResponder <UIApplicationDelegate>{
AVAudioPlayer *backgroundSound;
}
#property (strong, nonatomic) UIWindow *window;
#end
There are no errors and there is no looping sound.Kindly tell me what is going on.
Best Regards
ok i added [background play]; and it has started working fine..... that wasn't mentioned in the tutorial which i was following..
player = [[MPMoviePlayerController alloc] initWithContentURL:url];
player.movieSourceType = MPMovieSourceTypeStreaming;
if(player.playbackState == MPMoviePlaybackStatePlaying)
[player stop];
[player play];
I created a button using a play and stop image, what I want is when i press play and that the sound finishes, the stop image should go back to play, but it's not working, can anyone help here please. cheers.
- (IBAction)play {
if(clicked == 0){
clicked = 1;
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/TEST.wav", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 0;
[audioPlayer play];
[start setImage:[UIImage imageNamed:#"stop.png"] forState:UIControlStateNormal];
}
else{
[audioPlayer release];
clicked = 0;
[start setImage:[UIImage imageNamed:#"pla.png"] forState:UIControlStateNormal];
}
}
//If user does not do anything by the end of the sound set the button to start
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player
successfully: (BOOL) flag {
if (flag==YES) {
[start setImage:[UIImage imageNamed:#"pla.png"] forState:UIControlStateNormal];
}
}
This is my .h file
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
int clicked;
#interface _0_RabbanasViewController : UIViewController {
IBOutlet UIScrollView *scroll;
AVAudioPlayer *audioPlayer;
IBOutlet UIButton *start;
IBOutlet UIImage *iPla;
IBOutlet UIImage *iStop;
BOOL successfully;
}
- (IBAction)play;
- (IBAction)next;
#end
Make sure you set the delegate of the AVAudioPlayer instance to self:
[audioPlayer setDelegate:self];
and in the header file, subscribe to the AVAudioPlayerDelegate protocol:
<AVAudioPlayerDelegate>
Now it should work.
EDIT: Here's where to subscribe to the AVAudioPlayerDelegate protocol:
#interface _0_RabbanasViewController : UIViewController <AVAudioPlayerDelegate> {
IBOutlet UIScrollView *scroll;
AVAudioPlayer *audioPlayer;
IBOutlet UIButton *start;
...
Hope it works now. :)
EDIT2: Set audioPlayer delegate to self here:
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 0;
[audioPlayer setDelegate:self];
[audioPlayer play];
EDIT3: Add this:
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player
successfully: (BOOL) flag {
if (flag==YES) {
clicked = 0;
[start setImage:[UIImage imageNamed:#"pla.png"] forState:UIControlStateNormal];
}
}
I have been using a class to play sounds using AVAudioPlayer. Since I want to release these sounds right after they are played, I added a delegate. That causes a "_NSAutoreleaseNoPool(): Object 0x55e060 of class NSCFString autoreleased with no pool in place - just leaking" error right after the sound completes playing, but before my -audioPlayerDidFinishPlaying is called.
Here are some sources:
#interface MyAVAudioPlayer : NSObject <AVAudioPlayerDelegate> {
AVAudioPlayer *player;
float savedVolume;
BOOL releaseWhenDone;
}
The main class .m:
- (MyAVAudioPlayer *) initPlayerWithName: (NSString *) name;
{
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource: name ofType: #"caf"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
player = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error: nil];
[fileURL release];
[player prepareToPlay];
return (self);
}
- (MyAVAudioPlayer *)getAndPlayAndRelease:(NSString *)name withVolume:(float) vol;
{
MyAVAudioPlayer *newMyAVPlayer = [self initPlayerWithName:name];
player.volume = vol;
[player play];
releaseWhenDone = YES;
[player setDelegate: self];
return newMyAVPlayer;
}
+ (void) getAndPlayAndReleaseAuto:(NSString *)name withVolume:(float) vol;
{
MyAVAudioPlayer *newMyAVPlayer = [[MyAVAudioPlayer alloc] getAndPlayAndRelease:name withVolume:vol];
// [newMyAVPlayer autorelease];
}
#pragma mark -
#pragma mark AVAudioPlayer Delegate Methods
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)playedPlayer successfully:(BOOL)flag {
if (releaseWhenDone) {
NSLog(#"releasing");
[playedPlayer release];
// [self release];
NSLog(#"released");
}
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(#"Error while decoding: %#", [error localizedDescription] );
}
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
NSLog(#"Interrupted!");
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player {
NSLog(#"EndInterruption!");
}
- (BOOL) play;
{
player.currentTime = 0.0;
return [player play];
}
Commenting out the [player setDelegate: self]; makes the error go away, but then my audioPlayerDidFinishPlaying doesn't get called.
Any thoughts? Am I suddenly running in another thread?
I found the problem. My bug, of course.
In a lot of my class files, I was adding:
-(BOOL) respondsToSelector:(SEL) aSelector
{
NSLog(#"Class: %# subclass of %#, Selector: %#", [self class], [super class], NSStringFromSelector(aSelector));
return [super respondsToSelector:aSelector];
}
Mainly out of curiousity.
Well, when I added a delegate to my sound, then this method gets called before the delegate, and it gets called from whatever runloop the AVAudioPlayer happened to be in, and likely, a runloop with no autorelease pool.