play multiple SpeakHere audio files - ios

By recording multiple snippets using filenames, I have attempted to record multiple separate short voice snippets in SpeakHere, I want to play them serially, separated by a set fixed interval of time between the starts of each snippet. I want the series of snippets to play in a loop forever, or until the user stops play.
My question is how do I alter SpeakHere to do so?
(I say "attempted" because I have not been able yet to run SpeakHere on my Mac Mini iPhone simulator. That is the subject of another question and because another question on the subject of multiple files has not been answered, either.)
In SpeakHereController.mm is the following method definition for playing a recorded file. Notice the final else clause calls player->StartQueue(false)
- (IBAction)play:(id)sender
{
if (player->IsRunning())
{ [snip]
}
else
{
OSStatus result = player->StartQueue(false);
if (result == noErr)
[[NSNotificationCenter defaultCenter] postNotificationName:#"playbackQueueResumed" object:self];
}
}
Below is an excerpt from SpeakHere AQPlayer.mm
OSStatus AQPlayer::StartQueue(BOOL inResume)
{
// if we have a file but no queue, create one now
if ((mQueue == NULL) && (mFilePath != NULL)) CreateQueueForFile(mFilePath);
mIsDone = false;
// if we are not resuming, we also should restart the file read index
if (!inResume) {
mCurrentPacket = 0;
// prime the queue with some data before starting
for (int i = 0; i < kNumberBuffers; ++i) {
AQBufferCallback (this, mQueue, mBuffers[i]);
}
}
return AudioQueueStart(mQueue, NULL);
}
So, can the method play and AQPlayer::StartQueue be used to play the multiple files, how can the intervals be enforced, and how can the loop be repeated?
My adaptation of the code for the method 'record` is as follows, so you can see how the multiple files are being created.
- (IBAction)record:(id)sender
{
if (recorder->IsRunning()) // If we are currently recording, stop and save the file.
{
[self stopRecord];
}
else // If we're not recording, start.
{
self.counter = self.counter + 1 ; //Added *****
btn_play.enabled = NO;
// Set the button's state to "stop"
btn_record.title = #"Stop";
// Start the recorder
NSString *filename = [[NSString alloc] initWithFormat:#"recordedFile%d.caf",self.counter];
// recorder->StartRecord(CFSTR("recordedFile.caf"));
recorder->StartRecord((CFStringRef)filename);
[self setFileDescriptionForFormat:recorder->DataFormat() withName:#"Recorded File"];
// Hook the level meter up to the Audio Queue for the recorder
[lvlMeter_in setAq: recorder->Queue()];
}
}

Having spoken with a local "meetup" group on iOS I have learned that the easy solution to my question is to avoid AudioQueues and to instead use the "higher level" AVAudioRecorder and AVAudioPlayer from AVFoundation.
I also found how to partially test my app on the simulator with my Mac Mini: by plugging in an Olympus audio recorder with USB to my Mini as an input "voice". This works as an alternative to the iSight which does not produce an input audio on the Mini.

Related

no sound when using pjsip

I've got a problem with pjsip. I'm trying to make an outgoing call with pjsua_call_make_call. It's working, but when I answer this call on a device, I can't hear any sound. However, I can see an icon on iPhone, indicating that a microphone is in use. Did anybody come across such issue?
I am having a similar issue. I place an outbound call and I can hear the audio on the device when I pick up the call, but can't hear any audio on the device using pjsip to make the call.
If your audio capture doesn't seem to be working make sure you have microphone permission, and you have to manually call pjsua_set_snd_dev(), when you connect. There's some other additional troubleshooting here https://trac.pjsip.org/repos/wiki/Getting-Started/iPhone#Commonproblems
I've no experience with iOS, but I suppose you should connect audio stream to some device in on_call_media_state callback (link). Look at minimal example from desktop app:
pjsua_call_info ci;
pjsua_call_get_info(call_id, &ci);
for (unsigned i = 0; i < ci.media_cnt; i++) {
if (ci.media[i].type == PJMEDIA_TYPE_AUDIO) {
if (ci.media[i].status == PJSUA_CALL_MEDIA_ACTIVE) {
pjsua_conf_connect(ci.conf_slot, 0);
pjsua_conf_connect(0, ci.conf_slot);
}
}
}
Edit:
iOS code for default audio stream in call:
var callinfo: pjsua_call_info = pjsua_call_info()
pjsua_call_get_info(call_id, &callinfo)
if(callinfo.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
pjsua_conf_connect(callinfo.conf_slot, 0)
pjsua_conf_connect(0, callinfo.conf_slot)
}

How to update a UILabel in realtime when receiving Serial.print statements

I am using a Bluno microcontroller to send / receive data from an iPhone, and everything is working as it should, but I would like to update the text of a UILabel with the real time data that is being printed from the Serial.print(numTicks); statement. If I stop the flowmeter the UILabel gets updated with the most current value, but I would like to update this label in realtime. I am not sure if this is a C / Arduino question or more of a iOS / Objective-C question. The sketch I'm loading on my Bluno looks like the following, https://github.com/ipatch/KegCop/blob/master/KegCop-Bluno-sketch.c
And the method in question inside that sketch looks like the following,
// flowmeter stuff
bool getFlow4() {
// call the countdown function for pouring beer
// Serial.println(flowmeterPin);
flowmeterPinState = digitalRead(flowmeterPin);
// Serial.println(flowmeterPinStatePinState);
volatile unsigned long currentMillis = millis();
// if the predefined interval has passed
if (millis() - lastmillis >= 250) { // Update every 1/4 second
// disconnect flow meter from interrupt
detachInterrupt(0); // Disable interrupt when calculating
// Serial.print("Ticks:");
Serial.print(numTicks);
// numTicks = 0; // Restart the counter.
lastmillis = millis(); // Update lastmillis
attachInterrupt(0, count, FALLING); // enable interrupt
}
if(numTicks >= 475 || valveClosed == 1) {
close_valve();
numTicks = 0; // Restart the counter.
valveClosed = 0;
return 0;
}
}
On the iOS / Objective-C side of things I'm doing the following,
- (void)didReceiveData:(NSData *)data Device:(DFBlunoDevice *)dev {
// setup label to update
_ticks = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_tickAmount setText:[NSString stringWithFormat:#"Ticks:%#",_ticks]];
[_tickAmount setNeedsDisplay];
NSLog(#"ticks = %#",_ticks);
}
Basically I would like to update the value of the UILabel while the flowmeter is working.
UPDATE
I just tested the functionality again with the serial monitor within the Arduino IDE, and I got the same if not similar results as to what I got via Xcode and the NSLog statements. So this leads me to believe something in the sketch is preventing the label from updating in real time. :/ Sorry for the confusion.

iOS Voip Application | AudioQueue | AVSession Category

In my iOS Application , i am using AudioQueue for Audio recording and playback, basically i have OSX Version running and porting it on iOS.
I realize in iOS I need to configure / set the AV Session and i have done following till now,
-(void)initAudioSession{
//get your app's audioSession singleton object
AVAudioSession* session = [AVAudioSession sharedInstance];
//error handling
BOOL success;
NSError* error;
//set the audioSession category.
//Needs to be Record or PlayAndRecord to use audioRouteOverride:
success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
error:&error];
if (!success) NSLog(#"AVAudioSession error setting category:%#",error);
//set the audioSession override
success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker
error:&error];
if (!success) NSLog(#"AVAudioSession error overrideOutputAudioPort:%#",error);
//activate the audio session
success = [session setActive:YES error:&error];
if (!success) NSLog(#"AVAudioSession error activating: %#",error);
else NSLog(#"audioSession active");
}
Now what is happening is, Speaker AudioQueue callback is never getting called, i checked many answers, comments on so , google etc... and looks to be correct , the way i did is
Create AudioQueue for input and output : Configuration Linear PCM , 16000 Sampling rate
Allocate buffer
Setup queue with valid callback,
Start Queue,
It seems to be fine, i can able to hear Output on other end ( i.e. Input AudioQueue is working ) but output AudioQueue ( i.e. AudioQueueOutputCallback is never getting called).
I am suspecting i need to set the Proper AVSessionCatogery that i am trying with all possible option but didn't able to hear anything in the speaker,
I Compare my Implementation with Apple example Speakhere running AudioQueue on the main thread.
Even if i don't start Input AudioQueue ( mic ) then also i same behavior. and its difficult to have Speakhere behavior i.e. stop record and play
Thanks for looking at it, expecting your comments/help. Will be able to share code snippet.
Thanks for looking at it , i realize the problem, this is my callback,
void AudioStream::AQBufferCallback(void * inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inCompleteAQBuffer)
{
AudioStream *THIS = (AudioStream *)inUserData;
if (THIS->mIsDone) {
return;
}
if ( !THIS->IsRunning()){
NSLog(#" AudioQueue is not running");
**return;** // Error part
}
int bytes = THIS->bufferByteSize;
if ( !THIS->pSingleBuffer){
THIS->pSingleBuffer = new unsigned char[bytes];
}
unsigned char *buffer = THIS->pSingleBuffer;
if ((THIS->mNumPacketsToRead) > 0) {
/* lets read only firt packet */
memset(buffer,0x00,bytes);
float volume = THIS->volume();
if (THIS->volumeChange){
SInt16 *editBuffer = (SInt16 *)buffer;
// loop over every packet
for (int nb = 0; nb < (sizeof(buffer) / 2); nb++) {
// we check if the gain has been modified to save resoures
if (volume != 0) {
// we need more accuracy in our calculation so we calculate with doubles
double gainSample = ((double)editBuffer[nb]) / 32767.0;
/*
at this point we multiply with our gain factor
we dont make a addition to prevent generation of sound where no sound is.
no noise
0*10=0
noise if zero
0+10=10
*/
gainSample *= volume;
/**
our signal range cant be higher or lesser -1.0/1.0
we prevent that the signal got outside our range
*/
gainSample = (gainSample < -1.0) ? -1.0 : (gainSample > 1.0) ? 1.0 : gainSample;
/*
This thing here is a little helper to shape our incoming wave.
The sound gets pretty warm and better and the noise is reduced a lot.
Feel free to outcomment this line and here again.
You can see here what happens here http://silentmatt.com/javascript-function-plotter/
Copy this to the command line and hit enter: plot y=(1.5*x)-0.5*x*x*x
*/
gainSample = (1.5 * gainSample) - 0.5 * gainSample * gainSample * gainSample;
// multiply the new signal back to short
gainSample = gainSample * 32767.0;
// write calculate sample back to the buffer
editBuffer[nb] = (SInt16)gainSample;
}
}
}
else{
// NSLog(#" No change in the volume");
}
memcpy(inCompleteAQBuffer->mAudioData, buffer, 640);
inCompleteAQBuffer->mAudioDataByteSize = 640;
inCompleteAQBuffer->mPacketDescriptionCount = 320;
show_err(AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0, NULL));
}
}
as i was not enqueue when its allocated and i believe it had to enqueue few buffers before it gets started, removing the return part solved my problem.

AudioToolbox MusicPlayer change program has no effect

MIDI noob in training here...
I have been using MusicPlayer/MusicSequence/MusicTrack to play MIDI notes on devices running iOS. The notes are playing fine. I am struggling to change the instrument being played. As far as I can figure this is how to do it:
-(void) setInstrument:(MIDIInstruments) program channel:(int) channel MusicTrack:(MusicTrack*) track time:(float) time {
if(channel < 0 || channel > 15 || program >=MIDI_INSTRUMENT_COUNT || time < 0) {
return;
}
MIDIChannelMessage programChange = { ((UInt8)0xC) << 4 | ((UInt8)channel), ((UInt8)program), 0, 0};
OSStatus result = MusicTrackNewMIDIChannelEvent(*track, time, &programChange);
if(result != noErr) {
[NSException raise:#"Set Instrument" format:#"Failed to set instrument error: %#", [NSError errorWithDomain:NSOSStatusErrorDomain code:result userInfo:nil]];
}
}
In this case channel is 0 or 1, I tried several instruments through out the range of valid instrument enumerations, the time is 0.0, and the MusicTrack is valid, and has ~30 seconds of note events. The call to set the channel event passes back noErr. I am stumped...Anyone?
I had read in other posts that I would be able to generate midi using Music Player and friends. It provides for program changes. So, I had figured it was supported. After exhausting all theories, I turned to AUGraph. I added a *.sf2 file that I found online, instantiated the AUGraph, two AudioUnits, a MidiEndpointRef, and a MidiClientRef; according to this tutorial.
It was in the endpoint callback that I had to turn notes on and off using MusicDeviceMIDIEvent on the samplerUnit that seemed to allow for the program change. Whereas before I was just loading note events into a MusicTrack and playing/stoping the MusicPlayer.

AVFoundation.AVAudioPlayer stops randomly

I'm trying to play multiple sounds at the same time. However sometimes the sounds just stops playing or never starts at all.
I have an eventhandler that recieves an event when a sound effect should be played:
void HandlePlaySound (object sender, EventArgs e)
{
this.InvokeOnMainThread (()=>{
...
[set url to path]
...
MonoTouch.AVFoundation.AVAudioPlayer player = MonoTouch.AVFoundation.AVAudioPlayer.FromUrl(url);
player.Play();
});
}
This works fine most of the time but when two sounds gets triggered at the same time it's seems like one of them will be killed or both. I must be doing something really wrong here.
Is there a more correct way of playing sounds in an iPhone app. Each sound is supposed to play till end and there could be multiple sounds playing at the same time.
If I were to guess, I'd say that sometimes, the GC comes in and disposes the player that has gone out of scope, causing your random stop behaviour. I found a stable solution being first establishing how many simultaneous audio streams you'd like to able to play, and then enforcing those rules:
// I'd like a maximum of 5 simultaneous audio streams
Queue<AVAudioPlayer> players = new Queue<AVAudioPlayer>(5);
void PlayAudio (string fileName)
{
NSUrl url = NSUrl.FromFilename(fileName);
AVAudioPlayer player = AVAudioPlayer.FromUrl(url);
if (players.Count == 5) {
players.Dequeue().Dispose();
}
players.Enqueue(player);
player.Play();
}
// In my example, I'll select files from my Sounds folder (containing a couple of .wav, a couple of .mp3 and an .aif)
string[] files;
int fileIndex = 0;
string GetNextFileName ()
{
if (files == null)
files = Directory.GetFiles("Sounds");
if (fileIndex == files.Length)
fileIndex = 0;
return files[fileIndex++];
}
partial void OnPlayButtonTapped (NSObject sender)
{
string fileName = GetNextFileName();
PlayAudio(fileName);
}

Resources