I've searched Google and StackOverflow for two days and have yet to find a clear, concise answer.
My goal is to create a slider/progress bar that allows the user to scrub to a different part of the song, with two labels on either side, one showing the elapsed time and the other, the time left on the song.
I'm using DiracAudioPlayer, which also incorporates AVAudioPlayer functionality. Any help or direction to a tutorial is greatly appreciated.
Not sure if this is an issue with doing the scrubbing or updating the time elapsed so I'll show what I think you can do for both.
To update the progress bar and time labels, Apple does the following in their example project avTouch
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:player repeats:YES];
- (void)updateCurrentTime
{
currentTime.text = [NSString stringWithFormat:#"%d:%02d", (int)self.player.currentTime / 60, (int)p.currentTime % 60, nil];
progressBar.value = self.player.currentTime;
}
And then the seeking is simply done by setting the currentTime
- (IBAction)progressSliderMoved:(UISlider *)sender
{
player.currentTime = sender.value;
[self updateCurrentTimeForPlayer:player];
}
Related
I'm wondering the best approach to take for this.
The example app is:- I have a text field and button. click the button, initiates a task to update the text field. But the text field needs to be updated on a timer (or in background), say every 1 sec, but I only need the timer to run for say 5 secs, populating a random piece of text for example.
This should give the impression that the text box is displaying random words every sec, then after the 5 secs has completed, the timer can stop and the last value remains in the text box.
But I also want to detect the stop event and then pick up the value in the text field and perform another action.
Finally the question :-
Should I use Timer events, or operations and queues ? Not sure which approach is best.
Yes, use a timer in this way, repeats:YES is what you need:
[NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:#selector(ChangeText) userInfo:nil repeats:YES];
- (void) ChangeText
{
_textfield.text = [nsstring stringwithformat:#"%#%#", _textfield.text, _yourstring];
}
Yes , above answer is correct, just need some small changes according to your question...
count = 0;
[NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:#selector(ChangeText) userInfo:nil repeats:YES];
- (void) ChangeText
{
if (count < 5)
{
_textfield.text = [nsstring stringwithformat:#"%#%#", _textfield.text, _yourstring];
}
else
{
//Start your next event here;
}
NSTimer able to work only on main thread, then you don't need to expect that it will fire if it launched in background.
Also I think it's basically question of good architecture - don't you want try to handle events to update your textfield? For example, handle event when some task is finished or displayed value changed, then use delegate/block/NSNotification to receive event and update UI.
I play a MP3 in my iPhone app using AVAudioPlayer; i need to perform some operations at certain times (say 30th seconds, 1 minute); is there a way to invoke callback functions based on mp3 playing time?
I believe the best solution is to start an NSTimer as you start the AVAudioPlayer playing. You could set the timer to fire every half second or so. Then each time your timer fires, look at the currentTime property on your audio player.
In order to do something at certain intervals, I'd suggest you kept an instance variable for the playback time from last time your timer callback was called. Then if you had passed the critical point between last callback and this, do your action.
So, in pseudocode, the timer callback:
Get the currentTime of your AVAudioPlayer
Check to see if currentTime is greater than criticalPoint
If yes, check to see if lastCurrentTime is less than criticalPoint
If yes to that too, do your action.
Set lastCurrentTime to currentTime
If you're able to use AVPlayer instead of AVAudioPlayer, you can set boundary or periodic time observers:
// File URL or URL of a media library item
AVPlayer *player = [[AVPlayer alloc] initWithURL:url];
CMTime time = CMTimeMakeWithSeconds(30.0, 600);
NSArray *times = [NSArray arrayWithObject:[NSValue valueWithCMTime:time]];
id playerObserver = [player addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{
NSLog(#"Playback time is 30 seconds");
}];
[player play];
// remove the observer when you're done with the player:
[player removeTimeObserver:playerObserver];
AVPlayer documentation:
http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVPlayer_Class/Reference/Reference.html
I found this link describing a property property which seems to indicate you can get the current playback time.
If the sound is playing, currentTime is the offset of the current
playback position, measured in seconds from the start of the sound. If
the sound is not playing, currentTime is the offset of where playing
starts upon calling the play method, measured in seconds from the
start of the sound.
By setting this property you can seek to a specific point in a sound
file or implement audio fast-forward and rewind functions.
To check the time and perform your action you can simply query it:
if (avAudioPlayerObject.currentTime == 30.0) //You may need a more broad check. Double may not be able to exactly represent 30.0s
{
//Do Something
}
with multithreading your goal is simple, just do like this :
1 : in your main thread create a variable for storing time passed
2 : create new thread like "checkthread" that check each 30-20 sec(as you need)
3 : if the time passed is what you want do the callback
Yes Sure you can ...it's tricky i hope it works for you but it works for me ..
1- you play your mp3 file.
2- [self performSelector:#selector(Operation:) withObject:Object afterDelay:30];
then the function
-(void)Operation:(id)sender;
called; so you fired function after 30 second of mp3 file .. you can make many of function based on time you want..
3- there is other solution using timers
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(CheckTime:) userInfo:nil repeats:YES];
it will fire function called Check Time
-(void)CheckTime:(id)sender{
if (avAudioPlayerObject.currentTime == 30.0)
{
//Do Something
//Fire and function call such
[self performSelector:#selector(Operation:) withObject:Object]
}
}
then you can change time interval you want and repeats is for you to control repeat this action every 5 seconds or not..
Hope that helpful..
Thanks
i think ,you want to play different sound-files after 30sec then use this code :
1) all sound-files put in Array and then retrieve from document directory
2)then try this:
-(IBAction)play_sound
{
BackgroundPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:[Arr_tone_selected objectAtIndex:j]ofType:#"mp3"]]error:NULL];
BackgroundPlayer.delegate=self;
[BackgroundPlayer play];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[BackgroundPlayer stop];
j++;
[self performSelector:#selector(play_sound) withObject:Object afterDelay:30];
}
I'm working on a very simple iPhone game that involves choosing the right colored button as many times in a row based on a randomized voice prompt. I have it set up so that if the button is one color and gets clicked, it always goes to a hard-coded color every time (e.g. if you click red, it always turns blue). The color change method is set up in an IBOutlet. I have a timer set up in a while loop, and when the timer ends it checks if the player made the right selection. The problem is that the button color change does not occur until after the timer runs out, and this causes a problem with the method used to check the correct answer. Is there a way to make this color change happen instantly? From what I've searched I know it has something to do with storyboard actions not occurring until after code executes, but I haven't found anything with using a timer. Here is a section of the method that calls the timer if the answer is correct:
BOOL rightChoice = true;
int colorNum;
NSDate *startTime;
NSTimeInterval elapsed;
colorNum = [self randomizeNum:middle];
[self setTextLabel:colorNum];
while (rightChoice){
elapsed = 0.0;
startTime = [NSDate date];
while (elapsed < 2.0){
elapsed = [startTime timeIntervalSinceNow] * -1.0;
NSLog(#"elapsed time%f", elapsed);
}
rightChoice = [self correctChoice:middleStatus :colorNum];
colorNum = [self randomizeNum:middle];
}
One of two things stood out
You're using a while loop as a timer, don't do this - the operation is synchronous.
If this is run on the main thread, and you code doesn't return, your UI will update. The mantra goes: 'when you're not returning you're blocking.'
Cocoa has NSTimer which runs asynchronously - it is ideal here.
So let's get to grips with NSTimer (alternatively you can use GCD and save a queue to an ivar, but NSTimer seems the right way to go).
Make an ivar called timer_:
// Top of the .m file or in the .h
#interface ViewController () {
NSTimer *timer_;
}
#end
Make some start and stop functions. How you call these is up to you.
- (void)startTimer {
// If there's an existing timer, let's cancel it
if (timer_)
[timer_ invalidate];
// Start the timer
timer_ = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(onTimerFinish:)
userInfo:nil
repeats:NO];
}
- (void)onTimerFinish:(id)sender {
NSLog(#"Timer finished!");
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
- (void)stopTimer {
if (!timer_)
return;
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
And now
Put your timer test code in the onTimerFinish function.
Make an ivar that stores the current choice. Update this ivar when a choice is made and make the relevant changes to the UI. Call stopTimer if the stop condition is met.
In the onTimerFinished you can conditionally call and startTimer again if you desire.
Hope this helps!
I'm trying to implement a button that starts a timer after a random period of time (between 0-10s). While the timer is running it should update a label every 0.005s to show how much time has elapsed. The problem i'm having is 2-fold:
I'm not sure how to get the label to update with the elapsed time every 0.005s.
I'm having trouble getting the app to wait the random amount of time before starting timer. At present I'm using sleep(x) however it seems to cause the app to ignore all the other code in the if statement and causes the button image to freeze up (i.e. it looks like its still clicked).
Here is the code I have so far...
- (IBAction)buttonPressed:(id)sender
{
if ([buttonLabel.text isEqualToString:#"START"])
{
buttonLabel.text = #" "; // Clear the label
int startTime = arc4random() % 10; // Find the random period of time to wait
sleep(startTime); // Wait that period of time
startTime = CACurrentMediaTime(); // Set the start time
buttonLabel.text = #"STOP"; // Update the label
}
else
{
buttonLabel.text = #" ";
double stopTime = CACurrentMediaTime(); // Get the stop time
double timeTaken = stopTime - startTime; // Work out the period of time elapsed
}
}
If anyone has any suggestions on..
A) How to get the label to update with the elapsed time.
or
B) How to fix the 'delay' period from freezing up the app
... it would be really helpful as I'm pretty much stumped at this point. Thanks in advance.
You should use an NSTimer to do this. Try the code:
- (void)text1; {
buttonLabel.text = #" ";
}
- (void)text2; {
buttonLabel.text = #"STOP";
}
- (IBAction)buttonPressed:(id)sender; {
if ([buttonLabel.text isEqualToString:#"START"]) {
int startTime = arc4random() % 10; // Find the random period of time to wait
[NSTimer scheduledTimerWithTimeInterval:(float)startTime target:self selector:#selector(text2:) userInfo:nil repeats:NO];
}
else{
// I put 1.0f by default, but you could use something more complicated if you want.
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(text1:) userInfo:nil repeats:NO];
}
}
I'm not exactly sure how you want to update label based on the time, but if you post more code, or give an example, I'll post the code on how to do that, but it would just be using an NSTimer as well. Hope that Helps!
The answer to A could be:
Once the random amount of time has passed, (#MSgambel has a good suggestion), then execute:
timer = [NSTimer scheduledTimerWithTimeInterval:kGranularity target:self selector:#selector(periodicallyUpdateLabel) userInfo:nil repeats:YES];
(The above line could go into #MSgambel's -text2 method.)
That will call the -periodicallyUpdateLabel method once every kGranularity seconds, repeatedly. In that method, you could do things like update your label, check for user actions, or end the game if the time is up or some other condition has been met.
And here is the -periodicallyUpdateLabel method:
- (void)periodicallyUpdateView {
counter++;
timeValueLabel.text = [NSString stringWithFormat:#"%02d", counter];
}
You'll have to format the text differently to get what you want. Also, translate from the counter value to time using kGranularity. However, and this is what I found, there is only so many cpu cycles in iOS devices. Trying to go down to microsecond level made the interface sluggish and the time displayed started to drift from the actual time. In other words, you may have to limit your updates of the label to once every one hundredth of a second or tenths. Experiment.
HI , i have made simple application with 5 view controllers with some functionality .. what i want to do now is add a time at the main screen . and it should b running till i quit from application .. i will move to other view controllers also but that timer would b running .. how i will have this functionality ??
Check out the "Timers" section here: http://www.iphoneexamples.com/
Also, refer to Apple's NSTimer Documentation
The most practical way to do this is to fake it - that is, just store the start timestamp, and don't bother to continuously maintain any kind of timePassed variable. This is both easier to code, and actually more reliable since it's stable.
Store an NSDate for the instant the timer was started, and whenever you want to display or update the timer, use NSDate's timeIntervalSinceNow method, which returns the number of seconds passed as an NSTimeInterval, which is basically a typedef for a double. Note: this function returns a negative number when called on a timestamp in the past, which will be the case here.
If part of your display is showing this time, you can update it every second (or even more often) with an NSTimer object that periodically called one of your methods which updates the display.
Sample code:
// In the initialization code:
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:#selector(secondPassed:) userInfo:nil repeats:YES];
// Later:
// (This code assumes #minutes < 60.)
- (void) secondPassed: (NSTimer:) timer {
NSTimeInterval secondsPassed = -1 * [self.timerStart timeIntervalSinceNow];
int minutes = (int)(secondsPassed / 60);
int seconds = (int)(seconds - minutes * 60);
NSString* timerString = [NSString stringWithFormat:#"%02d:%02d",
minutes, seconds];
[self updateTimerDisplay:timerString];
}