iOS + ofSoundStream + Bluetooth Audio

Hey oF community!

I’ve got a funky problem that I’m having trouble working around and I thought I’d ask the collective wisdom of everyone here in case anyone has solved this issue.

I’m working on an iOS app that records and plays back audio. I used Mazbox’s brilliant wav file recorder and everything works really well. The problem I’m having is when I hook my device up via airplay to a apple TV or bluetooth speaker to play the sounds over some speakers the sound output still comes from my device. If I remove the ofSoundStreamSetup(0, 1); line then all the sound comes out of the speakers just fine.

Is there some way I can specify the output device while using ofSoundStream? I know that you can do setDeviceId but that doesn’t seem to do anything on ios like it does on linux of mac.

Thanks for the help!

Quick update.

In other apps if I pull up the small dashboard at the bottom of the ios device there are several airplay options, however in my app the only airplay sound output option is the iPad internal speakers.

This is only the case if I’ve called stream.setup( … );

Still investigating!

Further research.

I found this thread:iphone + mic / audio in wherein Marek makes and interesting point.

I’m now trying the sound output example for ios and it looks as if I can’t get it to output audio without having the headphones plugged in. Looks like the feedback protection feature that Marek mentions in the thread.

I now guess it’s worth noting that the sound I’m the sound I’m trying to record and process if not being processed on the way out. ie I am not using an audioOut method, just normal sound players. I also don’t need to be recording all the time, Perhaps a solution could be that I only open the ofSoundStream when I want to record and close it otherwise? I tried adding the lines stream.stop();
stream.close();

immediately after my stream setup but they did not have any effect. Am I correctly closing the sound stream?

So it looks to me as if it is not possible to playback over bluetooth at the same time as your take input recording audio.

You can find info about it here: https://forums.developer.apple.com/thread/5787
and here: https://forums.developer.apple.com/message/8370

Apparently you can only play out to bluetooth if your audio category is AVAudioSessionCategoryPlayback and not AVAudioSessionCategoryPlayAndRecord.

I suppose my next step is to figure out if I can change the Audio session category dynamically within the app so I can connect to the bluetooth speaker for playing and disconnect from it for recording. If anyone has any advice on this it would be much appreciated!

Just a note, this also makes sense why recording only works if you load the sounds before you open up the sound stream (as was mentioned in a few threads on this forum), because if you load them afterwards line 35 of AVSoundPlayer.m sets the category to AVAudioSessionCategoryPlayback!

Cool…

Solved it!

All I needed to do was include the following header: #import<AVFoundation/AVFoundation.h>

and call these methods when I want to start and stop recording(respectively)
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: nil];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];

Seems to work well. Hope this helps people!

1 Like

Hah! I thoroughly enjoyed reading this little conversation with yourself.

Any chance you could share an example of sending audio to a Bluetooth device?

Absolutely!

If you don’t want to record it’s fairly simple, all you do is play the audio normally. The key is ensuring that the AVAudioSession is in the correct category to allow for bluetooth playback. If you’re not doing any recording and aren’t opening an ofSoundStream it should work automatically. Just set your default output to be your bluetooth device via the ios gui.

If you want to do recording you’ll probably need to open an ofSoundStream will change your AVAudioSession Category to “AVAudioSessionCategoryPlayAndRecord” which will disable bluetooth connectivity. You just need to change it back by adding the line

[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];

after you’ve finished recording. In my app I added a handler class based on MazBox’s WavFile Recorder with the following methods:

#import <AVFoundation/AVFoundation.h>

//--------------------------------------------------------------

void soundRecording::startRecording(){
    
    if(!bIsRecording){
        
        string realfile = filePath + fileName;
        
        wavWriter.open(realfile, WAVFILE_WRITE);
        
        bIsRecording = true;
        [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: nil];
        
    }
    
}
//--------------------------------------------------------------
void soundRecording::stopRecording(){
    
    if(bIsRecording){
        
        wavWriter.close();
        
        bIsRecording = false;
        
        loadSample();
        [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];

    }
}

Most of that is either controlling the wavfile recorder or keeping track of some internal variables but the important lines are at the end or each which turn on and off recording for the device.

Remember, if you don’t want to do any recording just play sound as you normally would via an ofSoundPlayer and select the desired bluetooth device as your default audio output. It’s that simple!

You can also check out the full project on our github here:

all open source!