I am playing the .mp3 file by using AVaudioplayer:-
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:#"Speech"ofType:#"mp3"]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
audioPlayer.delegate = self;
audioPlayer.numberOfLoops=-1;
[audioPlayer prepareToPlay];
[audioPlayer play];
}
audioPlayer.meteringEnabled = YES;
audioPlayer.volume=0;
NSTimer *playerTimer = nil;
if (!playerTimer)
{
playerTimer = [NSTimer scheduledTimerWithTimeInterval:0.001
target:self selector:#selector(monitorAudioPlayer)
userInfo:nil
repeats:YES];
}
[audioPlayer updateMeters];
-(void)monitorAudioPlayer{
[audioPlayer updateMeters];
for (int i=0; i<audioPlayer.numberOfChannels; i++)
{
float power = [ audioPlayer averagePowerForChannel: i ];
float peak = [ audioPlayer peakPowerForChannel: i ];
}
}
And in written i get the float number of power for that audioplayer.
Is there any way to change the power of peak value of this audio player so that the sound get change to some funnysound or something other.
It is possible by using audiosession or some other thing.
I had done this by using DIRAC.
But in that i am not able to get the powervalue.AS it always return the static powervalue.
Thanks in advance.
The average and peak power levels are not writable; they are calculated characteristics of the sample data.
player.volume = x where x is greater than 1.0 will amplify the sound level before it comes out of the speakers. If you set it to a stupidly high number like 20 or 100, it will end up sounding pretty funny!
If you want to do any more funny stuff to the samples, I think you'll have to use a low level API like RemoteIO or AudioQueue.
Related
I have searched for suitable answers to my question but I did not find any helpful so far.
I want to record the decibel in the environment. If a specific threshold is exceeded the app shall play a sound or song file. Everything works fine so far but I have troubles to keep the app running in the background.
I have already added the attribute "Application does not run in the background" and set its value to "NO". I've read that one should add the "external-accessory" element to the "Required background modes". I added that too but still it does not work.
I am using the AVAudioRecorder to record the sound and the AVPlayer to play the sound/music file. First I used the MPMediaController iPodMusicPlayer but it throws an exception along with the attribute "Required background modes".
EDIT:
I am using xCode 4.5 with iOS 6
EDIT 2:
When I add the string viop to the "Required background modes" it seems to continue recording while in background. But it still does not play the music file when being in background. I also tried to add the "audio" value too but it did not help.
EDIT 3:
I've consulted the apples developer reference. It seems like you have to configure your AVAudioSession. With that it seems to work (link to reference). But now I have troubles in playing more than one file because as soon as the first track has finished playing, the app will go into suspended mode again. As far as I know there is no possibility to initialize the AVPlayer or AVAudioPlayer with more than one file. I used the delegate methode audioPlayerDidFinishPlaying:successfully: to set the next track but it did not work.
EDIT 4: Ok, one possibility is to avoid stopping the recorder, that is removing the [record stop] so that it even records the sound when music is played. It is a work around that works but still I appreciate any other (better) solution to this. A solution that doesn't need to keep the recorder running all the time.
the relevant code:
I initialize everything in the viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//[MPMusicPlayerController applicationMusicPlayer];
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
lowPassResults = -120.0;
thresholdExceeded = NO;
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: YES];
} else {
NSString* errorDescription = [error description];
NSLog(errorDescription);
}
}
The levelTimer Callback that is called every 0.03 seconds:
- (void)levelTimerCallback:(NSTimer *)timer {
//refreshes the average and peak power meters (the meter uses a logarithmic scale, with -160 being complete quiet and zero being maximum input
[recorder updateMeters];
const double ALPHA = 0.05;
float averagePowerForChannel = [recorder averagePowerForChannel:0];
//adjust the referential
averagePowerForChannel = averagePowerForChannel / 0.6;
//converts the values
lowPassResults = ALPHA * averagePowerForChannel + (1.0 - ALPHA) * lowPassResults;
float db = lowPassResults + 120;
db = db < 0? 0: db;
if(db >= THRESHOLD)
{
[self playFile];
}
}
Finally the playFile method which plays the music file:
- (void) playFile {
NSString* title = #"(You came down) For a day";
NSString* artist = #"Forge";
NSMutableArray *songItemsArray = [[NSMutableArray alloc] init];
MPMediaQuery *loadSongsQuery = [[MPMediaQuery alloc] init];
MPMediaPropertyPredicate *artistPredicate = [MPMediaPropertyPredicate predicateWithValue:artist forProperty:MPMediaItemPropertyArtist];
MPMediaPropertyPredicate *titlePredicate = [MPMediaPropertyPredicate predicateWithValue:title forProperty:MPMediaItemPropertyTitle];
[loadSongsQuery addFilterPredicate:artistPredicate];
[loadSongsQuery addFilterPredicate:titlePredicate];
NSArray *itemsFromGenericQuery = [loadSongsQuery items];
if([itemsFromGenericQuery count])
[songItemsArray addObject: [itemsFromGenericQuery objectAtIndex:0]];
if([songItemsArray count])
{
MPMediaItemCollection *collection = [[MPMediaItemCollection alloc] initWithItems:songItemsArray];
if ([collection count]) {
MPMediaItem* mpItem = [[collection items]objectAtIndex:0];
NSURL* mediaUrl = [mpItem valueForProperty:MPMediaItemPropertyAssetURL];
AVPlayerItem* item = [AVPlayerItem playerItemWithURL:mediaUrl];
musicPlayer = [[AVPlayer alloc] initWithPlayerItem:item];
[musicPlayer play];
}
}
}
Can anybody help me with my problem? Did I miss anything else?
Try this,
AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier task = 0;
task=[application beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"Expiration handler called %f",[application backgroundTimeRemaining]);
[application endBackgroundTask:task];
task=UIBackgroundTaskInvalid;
}];
}
I want to play more than two song in my app. how do I can do it using AVAudioPlayer ?
I was going to type up the answer, but google provides a perfectly good answer:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/27285-play-sequence-audio-files-avaudioplayer.html
The first piece of code from the page will just call all the sounds in sequence:
- (void)playSoundSequence {
// Make array containing audio file names
NSArray *theSoundArray = [NSArray arrayWithObjects:#"one",#"two",nil];
int totalSoundsInQueue = [theSoundArray count];
for (int i=0; i < totalSoundsInQueue; i) {
NSString *sound = [theSoundArray objectAtIndex:i];
// Wait until the audio player is not playing anything
while(![audioPlayer isPlaying]){
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:sound ofType:#"wav"]] error:NULL];
self.audioPlayer = player;
[player release];
[audioPlayer setVolume:1.0f];
[audioPlayer play];
//Increment for loop counter & move onto next iteration
i++;
}
}
}
But, you are probably better off using an AVAudioPlayerDelegate:
If you don't want to wait in the loop, you can also make your
controller an AVAudioPlayerDelegate and implement
Code:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
This will be called when the end of the audio file is reached, at
which point you would start playing the next audio file.
audioPlayerDidFinishPlaying will then be called again when that one
finishes playing, and you start the next one after that, etc.
I'm wanting to play just part of an audio file. This audio file contains 232 spoken words, I have a dictionary that contains the start time of each word. So I have the start and stop times I just can't find a way to stop at a given time or play the file for a certain duration. Any advice and/or sample code to help me would be much appreciated, thanks Jason.
So I've found a solution, there's a problem with how I get endTime but I sure I can fix it.
//Prepare to play
[audioPlayer prepareToPlay];
//Get current time from the array using selected word
audioPlayer.currentTime = [[wordsDict objectForKey:selectedWord] floatValue];
//Find end time - this is a little messy for now
int currentIndex = 0;
int count = 0;
for (NSString* item in wordsKeys) {
if ([item isEqualToString: selectedWord]) {
currentIndex = count+1;
}
count++;
}
//Store found end time
endTime = [[wordsDict objectForKey:[wordsKeys objectAtIndex:currentIndex]] floatValue];
//Start Timer
NSTimer * myAudioTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(checkCurrentTime)
userInfo:nil
repeats:YES]
//Now play audio
[audioPlayer play];
//Stop at endTime
- (void) checkCurrentTime {
if(audioPlayer.playing && audioPlayer.currentTime >= endTime)
[audioPlayer stop];
}
This should do what you want. you don't need a repeating timer that's thrashing the player.
NSString *myAudioFileInBundle = #"words.mp3";
NSTimeInterval wordStartsAt = 1.8;
NSTimeInterval wordEndsAt = 6.5;
NSString *filePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:myAudioFileInBundle];
NSURL *myFileURL = [NSURL fileURLWithPath:filePath];
AVAudioPlayer *myPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:myFileURL error:nil];
if (myPlayer)
{
[myPlayer setCurrentTime:wordStartsAt];
[myPlayer prepareToPlay];
[myPlayer play];
[NSTimer scheduledTimerWithTimeInterval:wordEndsAt-wordStartsAt target:myPlayer.autorelease selector:#selector(stop) userInfo:nil repeats:NO];
}
Try using AVPlayer instead and make use of
addBoundaryTimeObserverForTimes:queue:usingBlock:
What it does: Requests invocation of a block when specified times are traversed during normal playback.
https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayer_Class/index.html#//apple_ref/occ/instm/AVPlayer/addBoundaryTimeObserverForTimes:queue:usingBlock:
The AVAudioPlayer gives you some neat properties to work with:
currentTime: during playback you can rely on this property.
playAtTime : starts playing from a pre-defined time.
But first of all I would write some helpers for that:
#interface Word {
double startTime;
double endTime;
}
#property double startTime;
#property double endTime;
#end
This is just a class to simply working with the following method.
- (void)playWord:(Word *)aWord {
self.avPlayer.playAtTime = aWord.startTime;
[avPlayer prepareToPlay];
[avPlayer play];
while (avPlayer.playing) {
/*
This while loop could be dangerous as it could go on for a long time.
But because we're just evaluating words, it won't be as much power draining
*/
if(avPlayer.currentTime >= aWord.endTime;
[avPlayer stop];
}
}
I would suggest you to use an array or any other mechanism to automatically switch to the next word. Maybe your could also add a Previous and Next button for handling user input.
Please let me know if that worked for you.
HTH
I am having trouble getting an accurate meter reading from the AVAudioRecorder (testing on iPad).
It seems to work fine while the volume is rising, however a delay happens when the volume drops. For example: I speak into the mic and slowly raise my voice. The readings increment from -35.567 to -34.678 up to -10.579 as I would hope, but when I stop talking there is a delay of 1 - 2 seconds before it drops back down to -35.567 (or whatever it happens to be).
The NSLog continues to update from the loop but the meter number stays the same during the delay even though the sound has long ended.
I have added the gist of the code below and would be happy to supply full code if need be.
I initialize the recorder like so:
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error];
[audioSession setActive:YES error: &error];
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];
recordedTmpFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithString: #"Recording.caf"]]];
recorder = [[ AVAudioRecorder alloc] initWithURL:recordedTmpFile settings:recordSetting error:&error];
[recorder setDelegate:self];
[recorder prepareToRecord];
[recorder setMeteringEnabled:YES];
[recorder record];
and update the meter in a loop:
-(void) loop:(ccTime)dt
{
if(isRecording == YES)
{
//get volume levels
[recorder updateMeters];
float level = [recorder peakPowerForChannel:0];
NSLog(#"Vol: %f", level);
}
}
edited: I should also mention that I am using the Cocos2d schedule for the loop:
[self schedule:#selector(loop:)];
Any ideas why there would be such a long delay?
edited: I have tried using the average peak power and this has no delay. So I could possibly use that as a work around. However I would rather not use and averaged peak power and it would be nice to understand what is going on.
I'm sure that most have figured this out but if you want less lag on your metering you need to use AudioQueue or RemoteIO. See the better explanation here:
Confusion with meters in AVAudioRecorder
You can fix this by resetting the meteringEnabled property to YES.
yourRecorderName.meteringEnabled = YES
Call this every time you want the levels to reset to ambient levels. This takes about 0.02 seconds, and in that time the levels will briefly drop down to 0, or -120 dB before resetting to ambient.
Alternatively, you can use:
[yourRecorderName stop]
[yourRecorderName record]
This takes about 0.05 seconds, but the levels won't drop down to 0 in the wait time. In fact, nothing will happen because in this case, it actually takes the recorder object 0.05 seconds to stop and start recording again.
How about using a timer ? it would be mutch quicker
after
NSError* error
if (recorder) {
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: YES];
} else
NSLog(#" error %#",[error description]);
}
Where levelTimer is the NStimer that calls the function that does what you want(levelTimerCallback:), updates the meters, etc.
-(IBAction)levelTimerCallback:(NSTimer*)timer
{
[recorder updateMeters];
float level = [recorder peakPowerForChannel:0];
NSLog(#"Vol: %f", level);
}
I have using AVAudioPlayer in my app but the problem is that when i play the song from list it will played while before stopping the song i am again goes to song list then select another song then both song will start playing simultaneously so please tell me the code which will terminate my first song.
I use flag variable for initializing the song file---
fileURL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:#" Om Namo" ofType:#"aac"]];
self._player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self._player)
{
_fileName.text = [NSString stringWithFormat: #"%# (%d ch.)", [[self._player.url relativePath] lastPathComponent], self._player.numberOfChannels, nil];
[self updateViewForPlayerInfo];
[self updateViewForPlayerState];
}
[fileURL release];
break;
Play and push function---
- (void)startPlayback
{
if ([self._player play])
{
//[self stop];
[self updateViewForPlayerState];
self._player.delegate = self;
}
else
NSLog(#"Could not play %#\n", self._player.url);
}
- (void)pausePlayback
{
[self._player pause];
[self updateViewForPlayerState];
}
I also make stop function---
-(void)stop
{
[_player release];
self._player=nil;
}
Please help me .
Is correct my stop function.
and where i put our stop function or any other solution to fix this problem...
I can think of 3 things :
1) You can make code look like code by putting 4 spaces infront of each line. The reason you've gt no answers so far is probably because your question is almost unreadable ;)
2) In your stop method you might want to try this :
- (void) stop {
[_player stop];
self._player = nil;
}
3) Apple highly discourage using an underscore in front of member names. I've never run into problems but I'd rename _player to player.