How to set pan in IOS Audio Unit Framework - ios

Hello stack overflow users,
i want to change pan position using UISlided in my IOS application.
i am upgrading whole app which is currently using AudioStreamer of Matt Gallagher
To change the pan value in in AudioStreamer used below code.
AudioQueueRef audioQueue; // defined in AudioStreamer.h file
- (void)changePan:(float)newPan
{
OSStatus panErr = AudioQueueSetParameter( audioQueue, kAudioQueueParam_Pan, newPan);
NSLog(#" setting pan: %ld", panErr);
if( panErr )
NSLog(#"Error setting pan: %ld", panErr);
}
i am replacing AudioStreamer with StreamingKit which use AudioUnit
if i will get some help to make this thing done using StreamingKit or AudioUnit i will appreciate that.
P.S Let me know if anyone needs more info.
Thanks

Using AudioUnit API, you can simply set the kMultiChannelMixerParam_Pan property of the audio mixer unit to set the stereo pan:
AudioUnitParameterValue panValue = 0.9; // panned almost dead-right. possible values are between -1 and 1
int result = AudioUnitSetParameter(mixerUnit, kMultiChannelMixerParam_Pan, kAudioUnitScope_Input, 0, panValue, 0);
if (result == 0)
{
NSLog("success");
}
You may also need to retrieve the internal mixerUnit instance variable from inside STKAudioPlayer. You can try [audioPlayer valueForKey:#"_mixerUnit"] for that or implement a getter yourself inside StreamingKit's files.

Related

Timestamping in AUv3 MIDI plug-in for iOS

I am trying to understand how timestamping works for an AUv3 MIDI plug-in of type "aumi", where the plug-in sends MIDI events to a host. I cache the MIDIOutputEventBlockand the transportStateBlock properties into _outputEventBlock and _transportStateBlock in the allocateRenderResourcesAndReturnError method and use them in the internalRenderBlockmethod:
- (AUInternalRenderBlock)internalRenderBlock {
// Capture in locals to avoid Obj-C member lookups. If "self" is captured in render, we're doing it wrong. See sample code.
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *timestamp, AVAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList *outputData, const AURenderEvent *realtimeEventListHead, AURenderPullInputBlock pullInputBlock) {
// Transport State
if (_transportStateBlock) {
AUHostTransportStateFlags transportStateFlags;
_transportStateBlock(&transportStateFlags, nil, nil, nil);
if (transportStateFlags & AUHostTransportStateMoving) {
if (!playedOnce) {
// NOTE On!
unsigned char dataOn[] = {0x90,69,96};
_outputEventBlock(timestamp->mSampleTime, 0, 3, dataOn);
playedOnce = YES;
// NOTE Off!
unsigned char dataOff[] = {0x80,69,0};
_outputEventBlock(timestamp->mSampleTime+96000, 0, 3, dataOff);
}
}
else {
playedOnce = NO;
}
}
return noErr;
};
}
What this code is meant to do is to play the A4 note in a synthesizer at the host for 2 seconds (the sampling rate is 48KHz). What I get is a click sound. Experimenting some, I have tried delaying the start of the note on MIDI event by offsetting the _outputEventBlock AUEventSampleTime, but it plays the click sound as soon as the play button is pressed on the host.
Now, if I change the code to generate the note off MIDI event when the _transportStateFlags are indicating the state is "not moving" instead, then the note plays as soon as the play button is pressed and stops when the pause button is pressed, which would be the correct behavior. This tells me that my understanding of the AUEventSampleTime property in MIDIOutputEventBlock is flawed and that it cannot be used to schedule MIDI events for the host by adding offsets to it.
I see that there is another property scheduleMIDIEventBlock, and tried using this property instead but when I use it, there isn't any sound played.
Any clarification of how this all works would be greatly appreciated.

play multiple SpeakHere audio files

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.

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.

iOS - Generate and play indefinite, simple audio (sine wave)

I'm looking to build an incredibly simple application for iOS with a button that starts and stops an audio signal. The signal is just going to be a sine wave, and it's going to check my model (an instance variable for the volume) throughout its playback and change its volume accordingly.
My difficulty has to do with the indefinite nature of the task. I understand how to build tables, fill them with data, respond to button presses, and so on; however, when it comes to just having something continue on indefinitely (in this case, a sound), I'm a little stuck! Any pointers would be terrific!
Thanks for reading.
Here's a bare-bones application which will play a generated frequency on-demand. You haven't specified whether to do iOS or OSX, so I've gone for OSX since it's slightly simpler (no messing with Audio Session Categories). If you need iOS, you'll be able to find out the missing bits by looking into Audio Session Category basics and swapping the Default Output audio unit for the RemoteIO audio unit.
Note that the intention of this is purely to demonstrate some Core Audio / Audio Unit basics. You'll probably want to look into the AUGraph API if you want to start getting more complex than this (also in the interest of providing a clean example, I'm not doing any error checking. Always do error checking when dealing with Core Audio).
You'll need to add the AudioToolbox and AudioUnit frameworks to your project to use this code.
#import <AudioToolbox/AudioToolbox.h>
#interface SWAppDelegate : NSObject <NSApplicationDelegate>
{
AudioUnit outputUnit;
double renderPhase;
}
#end
#implementation SWAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// First, we need to establish which Audio Unit we want.
// We start with its description, which is:
AudioComponentDescription outputUnitDescription = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_DefaultOutput,
.componentManufacturer = kAudioUnitManufacturer_Apple
};
// Next, we get the first (and only) component corresponding to that description
AudioComponent outputComponent = AudioComponentFindNext(NULL, &outputUnitDescription);
// Now we can create an instance of that component, which will create an
// instance of the Audio Unit we're looking for (the default output)
AudioComponentInstanceNew(outputComponent, &outputUnit);
AudioUnitInitialize(outputUnit);
// Next we'll tell the output unit what format our generated audio will
// be in. Generally speaking, you'll want to stick to sane formats, since
// the output unit won't accept every single possible stream format.
// Here, we're specifying floating point samples with a sample rate of
// 44100 Hz in mono (i.e. 1 channel)
AudioStreamBasicDescription ASBD = {
.mSampleRate = 44100,
.mFormatID = kAudioFormatLinearPCM,
.mFormatFlags = kAudioFormatFlagsNativeFloatPacked,
.mChannelsPerFrame = 1,
.mFramesPerPacket = 1,
.mBitsPerChannel = sizeof(Float32) * 8,
.mBytesPerPacket = sizeof(Float32),
.mBytesPerFrame = sizeof(Float32)
};
AudioUnitSetProperty(outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&ASBD,
sizeof(ASBD));
// Next step is to tell our output unit which function we'd like it
// to call to get audio samples. We'll also pass in a context pointer,
// which can be a pointer to anything you need to maintain state between
// render callbacks. We only need to point to a double which represents
// the current phase of the sine wave we're creating.
AURenderCallbackStruct callbackInfo = {
.inputProc = SineWaveRenderCallback,
.inputProcRefCon = &renderPhase
};
AudioUnitSetProperty(outputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
0,
&callbackInfo,
sizeof(callbackInfo));
// Here we're telling the output unit to start requesting audio samples
// from our render callback. This is the line of code that starts actually
// sending audio to your speakers.
AudioOutputUnitStart(outputUnit);
}
// This is our render callback. It will be called very frequently for short
// buffers of audio (512 samples per call on my machine).
OSStatus SineWaveRenderCallback(void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
// inRefCon is the context pointer we passed in earlier when setting the render callback
double currentPhase = *((double *)inRefCon);
// ioData is where we're supposed to put the audio samples we've created
Float32 * outputBuffer = (Float32 *)ioData->mBuffers[0].mData;
const double frequency = 440.;
const double phaseStep = (frequency / 44100.) * (M_PI * 2.);
for(int i = 0; i < inNumberFrames; i++) {
outputBuffer[i] = sin(currentPhase);
currentPhase += phaseStep;
}
// If we were doing stereo (or more), this would copy our sine wave samples
// to all of the remaining channels
for(int i = 1; i < ioData->mNumberBuffers; i++) {
memcpy(ioData->mBuffers[i].mData, outputBuffer, ioData->mBuffers[i].mDataByteSize);
}
// writing the current phase back to inRefCon so we can use it on the next call
*((double *)inRefCon) = currentPhase;
return noErr;
}
- (void)applicationWillTerminate:(NSNotification *)notification
{
AudioOutputUnitStop(outputUnit);
AudioUnitUninitialize(outputUnit);
AudioComponentInstanceDispose(outputUnit);
}
#end
You can call AudioOutputUnitStart() and AudioOutputUnitStop() at will to start/stop producing audio. If you want to dynamically change the frequency, you can pass in a pointer to a struct containing both the renderPhase double and another one representing the frequency you want.
Be careful in the render callback. It's called from a realtime thread (not from the same thread as your main run loop). Render callbacks are subject to some fairly strict time requirements, which means that there's many things you Should Not Do in your callback, such as:
Allocate memory
Wait on a mutex
Read from a file on disk
Objective-C messaging (Yes, seriously.)
Note that this is not the only way to do this. I've only demonstrated it this way since you've tagged this core-audio. If you don't need to change the frequency you can just use the AVAudioPlayer with a pre-made sound file containing your sine wave.
There's also Novocaine, which hides a lot of this verbosity from you. You could also look into the Audio Queue API, which works fairly similar to the Core Audio sample I wrote but decouples you from the hardware a little more (i.e. it's less strict about how you behave in your render callback).

How to control hardware mic input gain/level on iPhone?

My audio-analysis function responds better on the iPad (2) than the iPhone (4). It seems sensitive to softer sounds on the iPad, whereas the iPhone requires much louder input to respond properly. Whether this is because of mic placement, different components, different software configurations or some other factor, I'd like to be able to control for it in my app.
Obviously I could just multiply all of my audio samples to programmatically apply gain. Of course that has a software cost too, so:
Is it possible to control the mic's gain from software in iOS, similarly to how it is in MacOS? I can't find any documentation on this but I'm hoping I'm just missing it somehow.
On ios6+ you can use AVAudioSession properties
CGFloat gain = sender.value;
NSError* error;
self.audioSession = [AVAudioSession sharedInstance];
if (self.audioSession.isInputGainSettable) {
BOOL success = [self.audioSession setInputGain:gain
error:&error];
if (!success){} //error handling
} else {
NSLog(#"ios6 - cannot set input gain");
}
On ios5 you can get/set audio input gain properties using AudioSession functions
UInt32 ui32propSize = sizeof(UInt32);
UInt32 f32propSize = sizeof(Float32);
UInt32 inputGainAvailable = 0;
Float32 inputGain = sender.value;
OSStatus err =
AudioSessionGetProperty(kAudioSessionProperty_InputGainAvailable
, &ui32propSize
, &inputGainAvailable);
if (inputGainAvailable) {
OSStatus err =
AudioSessionSetProperty(kAudioSessionProperty_InputGainScalar
, sizeof(inputGain)
, &inputGain);
} else {
NSLog(#"ios5 - cannot set input gain");
}
OSStatus err =
AudioSessionGetProperty(kAudioSessionProperty_InputGainScalar
, &f32propSize
, &inputGain);
NSLog(#"inputGain: %0.2f",inputGain);
(error handling omitted)
As you are interested in controlling input gain, you may also want to disable automatic gain control by setting the audio session mode to AVAudioSessionModeMeasurement (ios5+6)
[self.audioSession setMode:AVAudioSessionModeMeasurement
error:nil];
NSLog(#"mode:%#",self.audioSession.mode);
These settings are fairly hardware-specific so availability cannot be assumed. For example, I can alter the gain on iPhone3GS/ios6 and iPhone4S/ios5.1, but not on ipadMini/ios6.1. I can disable AGC on the iPhone3G and the iPad mini, but not the iPhone4S.
I think this can help you : http://www.stefanpopp.de/2011/capture-iphone-microphone/

Resources