FMOD and simple If statement - iphone

I have a simple set of FMOD actions that each play various sounds separately. The problem comes in that I have a button that stops all the sounds. FMOD crashes my program if we try to stop a sound that isn't playing. Makes sense. But I've decided that there has to be a way to call a simple if statement to find out "if" and only "if" somethings playing, then stop it.
- (IBAction)myButton:(id)sender {
if "FMOD CHANNEL IS PLAYING" {
result = fmodChannel->stop();
}
}
Any help would be appreciated.

I wouldn't say it “makes sense” that FMOD crashes if you call stop on a channel that isn't playing. It's documented to return an error code on failure. Are you sure fmodChannel is still a valid pointer when you call stop?
Anyway, you can try the isPlaying method:
bool isPlaying;
if (fmodChannel->isPlaying(&isPlaying) == FMOD_OK && isPlaying) {
fmodChannel->stop();
}

Related

Flutter record front facing camera at exact same time as playing video

I've been playing around with Flutter and trying to get it so I can record the front facing camera (using the camera plugin [https://pub.dev/packages/camera]) as well as playing a video to the user (using the video_player plugin [https://pub.dev/packages/video_player]).
Next I use ffmpeg to horizontally stack the videos together. This all works fine but when I play back the final output there is a slight delay when listening to the audio. I'm calling Future.wait([cameraController.startVideoRecording(filePath), videoController.play()]); but there is a slight delay in these tasks actually starting. I don't even need them to fire at the exact same time (which I'm realising is probably impossible), instead if I knew exactly when each of the tasks begun then I can use the time difference to sync the audio using ffmpeg or similar.
I've tried adding a listener on the videoController to see when isPlaying first returns true, and also watching the output directory for when the recorded video appears on the filesystem:
listener = () {
if (videoController.value.isPlaying) {
isPlaying = DateTime.now().microsecondsSinceEpoch;
log('isPlaying '+isPlaying.toString());
}
videoController.removeListener(listener);
};
videoController.addListener(listener);
var watcher = DirectoryWatcher('${extDir.path}/');
watcher.events.listen((event) {
if (event.type == ChangeType.ADD) {
fileAdded = DateTime.now().microsecondsSinceEpoch;
log('added '+fileAdded.toString());
}
});
Then likewise for checking if the camera is recording:
var listener;
listener = () {
if (cameraController.value.isRecordingVideo) {
log('isRecordingVideo '+DateTime.now().microsecondsSinceEpoch.toString());
//cameraController.removeListener(listener);
}
};
cameraController.addListener(listener);
This results in (for example) the following order and microseconds for each event:
is playing: 1606478994797247
is recording: 1606478995492889 (695642 microseconds later)
added: 1606478995839676 (346787 microseconds later)
However, when I play back the video the syncing is off by approx 0.152 seconds so doesn't marry up to the time differences reported above.
Does anyone have any idea how I could accomplish near perfect syncing when combining 2 videos? Thanks.

AudioQueue PropertyListener IsRunning only callback once

OSStatus err = AudioQueueNewOutput(&audioDescription, AudioPlayerAQOutputCallback, ( void* )self, nil, nil, 0, &audioQueue);
if( err != noErr )
NSLog(#"Couldn't open AudioFile.");
err = AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning, isRunningProc, self);
if( err != noErr )
NSLog(#"Couldn't register for playback state changes.");
this callback function only be called once after AudioQueueStart(audioQueue, NULL);
what ever i call AudioQueuePause(audioQueue);
or audio reach to end.
static void isRunningProc(void * inUserData,
AudioQueueRef inAQ,
AudioQueuePropertyID inID)
what i have missed?
I did a short test on this:
It does indeed seem like the callback it is not called either for pause or for start, when you are resuming a pause.
But this is not something you cannot solve. You started the song somehow. This will trigger the property listener. Equally if the song stops. Or you stop it. You may have to trigger the property listener yourself somehow using something like this in your play routine:
if (bytesRead == 0) {
//This will trigger the property listener
AudioQueueStop(inAQ, false);
}
else {
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
}
As AudioQueue is concerned, as long as you keep feeding it with audio buffers to play, it is still playing. (I also tested not feeding any buffers at all, which did not trigger a stop, so you have to call stop explicitly to trigger the property listener.)
This means you already know whether your song is playing or not. To pause or un-pause is requested by clicking your button. If the song is not playing, don't do anything. If the song is playing, call AudioQueuePause and set a flag that you have paused your music. Remember to check the error code. (See (1) below). If the flag says that you have paused the music, call AudioQueueStart, and clear the flag indicating if you have paused or not. Again check the error code.
(1) Why check the error code?
First, although unlikely, an error may occur because it is a blue moon.
However, my concern is with multiple threads. AudioQueue obviously runs on a separate thread than your GUI. That means if you test a flag whether music is playing or not, this state cannot fully be trusted because it might have changed since you tested the status. Another thread might have snuck in between your test and your action based on that test.
Say you check that the song is already playing. (It is.)
Then you ask the song to pause, but the song is really stopped since it reached the end in the meantime before you got to ask the song to pause.
Then you ask to pause the song. But it is already stopped.
What happens then? I don't really know. It might not even be a problem in this situation, but things like this is worth considering. It needs testing, or at least a consulting with the documentation.
How about another scenario? What if the song is stopped and you ask to start it again. I would think that is a worse scenario, but it might not be a problem. Again consider those cases and check the documentation, or even test yourself.

Speakhere does NOT record after receive a phone call

I'm working on a project that need record and analyze sound, every think is ok when i use speak here.
But when some one call my phone, the record stop and when the app return, it never record again.
I try to restart the recorder by press record, but i get this error:
Error: couldn't get input channel count ('!cat')
Error: couldn't enable metering (-50)
ERROR: metering failed
I also try to restart by call StartRecord(....) but nothing different. So anyone can help me
if (inInterruptionState == kAudioSessionEndInterruption)
THIS->recorder->StartRecord(CFSTR("recordedFile.caf"));
An app must stop recording in any audio session interrupt listener begin interruption callback if it ever wants to start recording again. Otherwise, a force quit and restart by the user may be required.
I've been having the same problem with SpeakHere and found this solution by (hours and hours of) trial and error. Try this: get rid of the references to playbackWasInterrupted (commented out below), but leave in the other player-related directives. Somehow this re-enables the recorder! If anyone could explain why this works, I would love to know!
Under void interruptionListener, change
else if ((inInterruptionState == kAudioSessionEndInterruption)&& THIS->playbackWasInterrupted))
to
else if (inInterruptionState == kAudioSessionEndInterruption)
//&& THIS->playbackWasInterrupted)
and then comment out or delete the "playbackWasInterrupted" line below:
{
// we were playing back when we were interrupted, so reset and resume now
THIS->player->StartQueue(true);
[[NSNotificationCenter defaultCenter] postNotificationName:#"playbackQueueResumed" object:THIS];
// THIS->playbackWasInterrupted = NO;
}
just from memory - when returning to foreground (in the corresponding notification handler), you need to call
AudioSessionSetActive (true)
or something similar. As I said, I only read it on a related question - no garanties.
Good Luck, nobi

Best way to implement MPMoviePlayer timeout

I have MPMoviePlayer that load movie from stream. I implemented timeout on 15 seconds with timers. But is there maybe some other better way to implement timeout without timer?
Register for the MPMoviePlayerLoadStateDidChangeNotification. Within its handler, check the current loadstate and mask out the MPMovieLoadStateStalled.
- (void)MPMoviePlayerLoadStateDidChange:(NSNotification *)notification
{
//is the player stalled, hence possibly starving?
if ((movieController_.loadState & MPMovieLoadStateStalled) == MPMovieLoadStateStalled)
{ //yes->do something
NSLog(#"hey there, I am starving to death here");
}
}
You may want to register a timer within the upper if-clause - e.g. 10seconds. Once that baby runs out of time without further state-changes, do something to terminate / skip the video playback.
I'm not sure but i think it's possible to use performSelector as timer?
[self performSelector:#selector(checkTimeout:) withObject:theMovie afterDelay:15];
and then check for the movie state.

iPhone App Pick Up Sound

I am trying to do a certain action based on whether or not the user makes a loud sound. I'm not trying to do any voice recognition or anything. Just simply do an action based on whether the iPhone picks up a loud sound.
Any suggestions, tutorials, I can't find anything on the apple developer site. I'm assuming i'm not looking or searching right.
The easiest thing for you do is to use the AudioQueue services. Here's the manual:
Apple AQ manual
Basically, look for any example code that initialized things with AudioQueueNewInput(). Something like this:
Status = AudioQueueNewInput(&_Description,
Audio_Input_Buffer_Ready,
self,
NULL,
NULL,
0,
&self->Queue);
Once you have that going, you can enable sound level metering with something like this:
// Turn on level metering (iOS 2.0 and later)
UInt32 on = 1;
AudioQueueSetProperty(self->Queue,kAudioQueueProperty_EnableLevelMetering,&on,sizeof(on));
You will have a callback routine that is invoked for each chunk of audio data. In it, you can check the current meter levels with something like this:
//
// Check metering levels and detect silence
//
AudioQueueLevelMeterState meters[1];
UInt32 dlen = sizeof(meters);
Status = AudioQueueGetProperty(_Queue,kAudioQueueProperty_CurrentLevelMeterDB,meters,&dlen);
if (Status == 0) {
if (meters[0].mPeakPower > _threshold) {
silence = 0.0; // reset silence timer
} else {
silence += time;
}
}
//
// Notify observers of incoming data.
//
if (delegate) {
[delegate audioMeter:meters[0].mPeakPower duration:time];
[delegate audioData:Buffer->mAudioData size:Buffer->mAudioDataByteSize];
}
Or, in your case, instead of silence you can detect if the decibel level is over a certain value for long enough. Note that the decibel values you will see will range from about -70.0 for dead silence, up to 0.0db for very loud things. On an exponential scale. You'll have to play with it to see what values work for your particular application.
Apple has examples such as Speak Here which looks to have code relating to decibels. I would check some of the meter classes for examples. I have no audio programming experience but hopefully that will get you started while someone provides you with a better answer.