[DEV] IOS work for output
This commit is contained in:
parent
b632782e5b
commit
bcf636ce0d
@ -218,3 +218,4 @@ bool airtaudio::api::Android::probeDeviceOpen(uint32_t _device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -41,12 +41,10 @@ namespace airtaudio {
|
|||||||
airtaudio::format _format,
|
airtaudio::format _format,
|
||||||
uint32_t *_bufferSize,
|
uint32_t *_bufferSize,
|
||||||
airtaudio::StreamOptions *_options);
|
airtaudio::StreamOptions *_options);
|
||||||
private:
|
public:
|
||||||
void callBackEvent(void* _data,
|
void callBackEvent(void* _data,
|
||||||
int32_t _frameRate);
|
int32_t _frameRate);
|
||||||
static void androidCallBackEvent(void* _data,
|
private:
|
||||||
int32_t _frameRate,
|
|
||||||
void* _userData);
|
|
||||||
CoreIosPrivate* m_private;
|
CoreIosPrivate* m_private;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -19,308 +19,20 @@ airtaudio::Api* airtaudio::api::CoreIos::Create(void) {
|
|||||||
return new airtaudio::api::CoreIos();
|
return new airtaudio::api::CoreIos();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================================================
|
|
||||||
|
|
||||||
@interface IosAudioController : NSObject {
|
|
||||||
AudioComponentInstance audioUnit;
|
|
||||||
AudioBuffer tempBuffer; // this will hold the latest data from the microphone
|
|
||||||
}
|
|
||||||
|
|
||||||
@property (readonly) AudioComponentInstance audioUnit;
|
|
||||||
@property (readonly) AudioBuffer tempBuffer;
|
|
||||||
|
|
||||||
- (void) start;
|
|
||||||
- (void) stop;
|
|
||||||
- (void) processAudio: (AudioBufferList*) bufferList;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================================================
|
|
||||||
|
|
||||||
#define kOutputBus 0
|
#define kOutputBus 0
|
||||||
#define kInputBus 1
|
#define kInputBus 1
|
||||||
|
|
||||||
IosAudioController* iosAudio;
|
|
||||||
|
|
||||||
void checkStatus(int status){
|
|
||||||
if (status) {
|
|
||||||
printf("Status not 0! %d\n", status);
|
|
||||||
// exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
This callback is called when new audio data from the microphone is
|
|
||||||
available.
|
|
||||||
*/
|
|
||||||
static OSStatus recordingCallback(void *inRefCon,
|
|
||||||
AudioUnitRenderActionFlags *ioActionFlags,
|
|
||||||
const AudioTimeStamp *inTimeStamp,
|
|
||||||
UInt32 inBusNumber,
|
|
||||||
UInt32 inNumberFrames,
|
|
||||||
AudioBufferList *ioData) {
|
|
||||||
// Because of the way our audio format (setup below) is chosen:
|
|
||||||
// we only need 1 buffer, since it is mono
|
|
||||||
// Samples are 16 bits = 2 bytes.
|
|
||||||
// 1 frame includes only 1 sample
|
|
||||||
|
|
||||||
AudioBuffer buffer;
|
|
||||||
|
|
||||||
buffer.mNumberChannels = 1;
|
|
||||||
buffer.mDataByteSize = inNumberFrames * 2;
|
|
||||||
buffer.mData = malloc( inNumberFrames * 2 );
|
|
||||||
|
|
||||||
// Put buffer in a AudioBufferList
|
|
||||||
AudioBufferList bufferList;
|
|
||||||
bufferList.mNumberBuffers = 1;
|
|
||||||
bufferList.mBuffers[0] = buffer;
|
|
||||||
|
|
||||||
// Then:
|
|
||||||
// Obtain recorded samples
|
|
||||||
|
|
||||||
OSStatus status;
|
|
||||||
|
|
||||||
status = AudioUnitRender([iosAudio audioUnit],
|
|
||||||
ioActionFlags,
|
|
||||||
inTimeStamp,
|
|
||||||
inBusNumber,
|
|
||||||
inNumberFrames,
|
|
||||||
&bufferList);
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
// Now, we have the samples we just read sitting in buffers in bufferList
|
|
||||||
// Process the new data
|
|
||||||
[iosAudio processAudio:&bufferList];
|
|
||||||
|
|
||||||
// release the malloc'ed data in the buffer we created earlier
|
|
||||||
free(bufferList.mBuffers[0].mData);
|
|
||||||
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
This callback is called when the audioUnit needs new data to play through the
|
|
||||||
speakers. If you don't have any, just don't write anything in the buffers
|
|
||||||
*/
|
|
||||||
static OSStatus playbackCallback(void *inRefCon,
|
|
||||||
AudioUnitRenderActionFlags *ioActionFlags,
|
|
||||||
const AudioTimeStamp *inTimeStamp,
|
|
||||||
UInt32 inBusNumber,
|
|
||||||
UInt32 inNumberFrames,
|
|
||||||
AudioBufferList *ioData) {
|
|
||||||
// Notes: ioData contains buffers (may be more than one!)
|
|
||||||
// Fill them up as much as you can. Remember to set the size value in each buffer to match how
|
|
||||||
// much data is in the buffer.
|
|
||||||
|
|
||||||
for (int i=0; i < ioData->mNumberBuffers; i++) { // in practice we will only ever have 1 buffer, since audio format is mono
|
|
||||||
AudioBuffer buffer = ioData->mBuffers[i];
|
|
||||||
|
|
||||||
// NSLog(@" Buffer %d has %d channels and wants %d bytes of data.", i, buffer.mNumberChannels, buffer.mDataByteSize);
|
|
||||||
|
|
||||||
// copy temporary buffer data to output buffer
|
|
||||||
UInt32 size = std::min(buffer.mDataByteSize, [iosAudio tempBuffer].mDataByteSize); // dont copy more data then we have, or then fits
|
|
||||||
memcpy(buffer.mData, [iosAudio tempBuffer].mData, size);
|
|
||||||
buffer.mDataByteSize = size; // indicate how much data we wrote in the buffer
|
|
||||||
|
|
||||||
// uncomment to hear random noise
|
|
||||||
/*
|
|
||||||
UInt16 *frameBuffer = buffer.mData;
|
|
||||||
for (int j = 0; j < inNumberFrames; j++) {
|
|
||||||
frameBuffer[j] = rand();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation IosAudioController
|
|
||||||
|
|
||||||
@synthesize audioUnit, tempBuffer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize the audioUnit and allocate our own temporary buffer.
|
|
||||||
The temporary buffer will hold the latest data coming in from the microphone,
|
|
||||||
and will be copied to the output when this is requested.
|
|
||||||
*/
|
|
||||||
- (id) init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
OSStatus status;
|
|
||||||
|
|
||||||
// Describe audio component
|
|
||||||
AudioComponentDescription desc;
|
|
||||||
desc.componentType = kAudioUnitType_Output;
|
|
||||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
|
||||||
desc.componentFlags = 0;
|
|
||||||
desc.componentFlagsMask = 0;
|
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
||||||
|
|
||||||
// Get component
|
|
||||||
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
|
|
||||||
|
|
||||||
// Get audio units
|
|
||||||
status = AudioComponentInstanceNew(inputComponent, &audioUnit);
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
// Enable IO for recording
|
|
||||||
UInt32 flag = 1;
|
|
||||||
status = AudioUnitSetProperty(audioUnit,
|
|
||||||
kAudioOutputUnitProperty_EnableIO,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
kInputBus,
|
|
||||||
&flag,
|
|
||||||
sizeof(flag));
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
// Enable IO for playback
|
|
||||||
status = AudioUnitSetProperty(audioUnit,
|
|
||||||
kAudioOutputUnitProperty_EnableIO,
|
|
||||||
kAudioUnitScope_Output,
|
|
||||||
kOutputBus,
|
|
||||||
&flag,
|
|
||||||
sizeof(flag));
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
// Describe format
|
|
||||||
AudioStreamBasicDescription audioFormat;
|
|
||||||
audioFormat.mSampleRate = 44100.00;
|
|
||||||
audioFormat.mFormatID = kAudioFormatLinearPCM;
|
|
||||||
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
|
||||||
audioFormat.mFramesPerPacket = 1;
|
|
||||||
audioFormat.mChannelsPerFrame = 1;
|
|
||||||
audioFormat.mBitsPerChannel = 16;
|
|
||||||
audioFormat.mBytesPerPacket = 2;
|
|
||||||
audioFormat.mBytesPerFrame = 2;
|
|
||||||
|
|
||||||
// Apply format
|
|
||||||
status = AudioUnitSetProperty(audioUnit,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kAudioUnitScope_Output,
|
|
||||||
kInputBus,
|
|
||||||
&audioFormat,
|
|
||||||
sizeof(audioFormat));
|
|
||||||
checkStatus(status);
|
|
||||||
status = AudioUnitSetProperty(audioUnit,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
kOutputBus,
|
|
||||||
&audioFormat,
|
|
||||||
sizeof(audioFormat));
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
|
|
||||||
// Set input callback
|
|
||||||
AURenderCallbackStruct callbackStruct;
|
|
||||||
callbackStruct.inputProc = recordingCallback;
|
|
||||||
callbackStruct.inputProcRefCon = self;
|
|
||||||
status = AudioUnitSetProperty(audioUnit,
|
|
||||||
kAudioOutputUnitProperty_SetInputCallback,
|
|
||||||
kAudioUnitScope_Global,
|
|
||||||
kInputBus,
|
|
||||||
&callbackStruct,
|
|
||||||
sizeof(callbackStruct));
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
// Set output callback
|
|
||||||
callbackStruct.inputProc = playbackCallback;
|
|
||||||
callbackStruct.inputProcRefCon = self;
|
|
||||||
status = AudioUnitSetProperty(audioUnit,
|
|
||||||
kAudioUnitProperty_SetRenderCallback,
|
|
||||||
kAudioUnitScope_Global,
|
|
||||||
kOutputBus,
|
|
||||||
&callbackStruct,
|
|
||||||
sizeof(callbackStruct));
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
// Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
|
|
||||||
flag = 0;
|
|
||||||
status = AudioUnitSetProperty(audioUnit,
|
|
||||||
kAudioUnitProperty_ShouldAllocateBuffer,
|
|
||||||
kAudioUnitScope_Output,
|
|
||||||
kInputBus,
|
|
||||||
&flag,
|
|
||||||
sizeof(flag));
|
|
||||||
|
|
||||||
// Allocate our own buffers (1 channel, 16 bits per sample, thus 16 bits per frame, thus 2 bytes per frame).
|
|
||||||
// Practice learns the buffers used contain 512 frames, if this changes it will be fixed in processAudio.
|
|
||||||
tempBuffer.mNumberChannels = 1;
|
|
||||||
tempBuffer.mDataByteSize = 512 * 2;
|
|
||||||
tempBuffer.mData = malloc( 512 * 2 );
|
|
||||||
|
|
||||||
// Initialise
|
|
||||||
status = AudioUnitInitialize(audioUnit);
|
|
||||||
checkStatus(status);
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Start the audioUnit. This means data will be provided from
|
|
||||||
the microphone, and requested for feeding to the speakers, by
|
|
||||||
use of the provided callbacks.
|
|
||||||
*/
|
|
||||||
- (void) start {
|
|
||||||
ATA_INFO("Might start the stream ...");
|
|
||||||
OSStatus status = AudioOutputUnitStart(audioUnit);
|
|
||||||
checkStatus(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Stop the audioUnit
|
|
||||||
*/
|
|
||||||
- (void) stop {
|
|
||||||
ATA_INFO("Might stop the stream ...");
|
|
||||||
OSStatus status = AudioOutputUnitStop(audioUnit);
|
|
||||||
checkStatus(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Change this funtion to decide what is done with incoming
|
|
||||||
audio data from the microphone.
|
|
||||||
Right now we copy it to our own temporary buffer.
|
|
||||||
*/
|
|
||||||
- (void) processAudio: (AudioBufferList*) bufferList{
|
|
||||||
AudioBuffer sourceBuffer = bufferList->mBuffers[0];
|
|
||||||
|
|
||||||
// fix tempBuffer size if it's the wrong size
|
|
||||||
if (tempBuffer.mDataByteSize != sourceBuffer.mDataByteSize) {
|
|
||||||
free(tempBuffer.mData);
|
|
||||||
tempBuffer.mDataByteSize = sourceBuffer.mDataByteSize;
|
|
||||||
tempBuffer.mData = malloc(sourceBuffer.mDataByteSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy incoming audio data to temporary buffer
|
|
||||||
memcpy(tempBuffer.mData, bufferList->mBuffers[0].mData, bufferList->mBuffers[0].mDataByteSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Clean up.
|
|
||||||
*/
|
|
||||||
- (void) dealloc {
|
|
||||||
[super dealloc];
|
|
||||||
AudioUnitUninitialize(audioUnit);
|
|
||||||
free(tempBuffer.mData);
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================================================
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace airtaudio {
|
namespace airtaudio {
|
||||||
namespace api {
|
namespace api {
|
||||||
class CoreIosPrivate {
|
class CoreIosPrivate {
|
||||||
|
public:
|
||||||
|
AudioComponentInstance audioUnit;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
airtaudio::api::CoreIos::CoreIos(void) :
|
airtaudio::api::CoreIos::CoreIos(void) :
|
||||||
m_private(new airtaudio::api::CoreIosPrivate) {
|
m_private(new airtaudio::api::CoreIosPrivate) {
|
||||||
ATA_INFO("new CoreIos");
|
ATA_INFO("new CoreIos");
|
||||||
@ -353,6 +65,7 @@ airtaudio::api::CoreIos::CoreIos(void) :
|
|||||||
|
|
||||||
airtaudio::api::CoreIos::~CoreIos(void) {
|
airtaudio::api::CoreIos::~CoreIos(void) {
|
||||||
ATA_INFO("Destroy CoreIOs interface");
|
ATA_INFO("Destroy CoreIOs interface");
|
||||||
|
AudioUnitUninitialize(m_private->audioUnit);
|
||||||
delete m_private;
|
delete m_private;
|
||||||
m_private = NULL;
|
m_private = NULL;
|
||||||
}
|
}
|
||||||
@ -375,30 +88,41 @@ enum airtaudio::errorType airtaudio::api::CoreIos::closeStream(void) {
|
|||||||
|
|
||||||
enum airtaudio::errorType airtaudio::api::CoreIos::startStream(void) {
|
enum airtaudio::errorType airtaudio::api::CoreIos::startStream(void) {
|
||||||
ATA_INFO("Start Stream");
|
ATA_INFO("Start Stream");
|
||||||
|
OSStatus status = AudioOutputUnitStart(m_private->audioUnit);
|
||||||
// Can not close the stream now...
|
// Can not close the stream now...
|
||||||
return airtaudio::errorNone;
|
return airtaudio::errorNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum airtaudio::errorType airtaudio::api::CoreIos::stopStream(void) {
|
enum airtaudio::errorType airtaudio::api::CoreIos::stopStream(void) {
|
||||||
ATA_INFO("Stop stream");
|
ATA_INFO("Stop stream");
|
||||||
//ewol::Context& tmpContext = ewol::getContext();
|
OSStatus status = AudioOutputUnitStop(m_private->audioUnit);
|
||||||
//tmpContext.audioCloseDevice(0);
|
|
||||||
// Can not close the stream now...
|
// Can not close the stream now...
|
||||||
return airtaudio::errorNone;
|
return airtaudio::errorNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum airtaudio::errorType airtaudio::api::CoreIos::abortStream(void) {
|
enum airtaudio::errorType airtaudio::api::CoreIos::abortStream(void) {
|
||||||
ATA_INFO("Abort Stream");
|
ATA_INFO("Abort Stream");
|
||||||
[iosAudio stop];
|
OSStatus status = AudioOutputUnitStop(m_private->audioUnit);
|
||||||
//ewol::Context& tmpContext = ewol::getContext();
|
|
||||||
//tmpContext.audioCloseDevice(0);
|
|
||||||
// Can not close the stream now...
|
// Can not close the stream now...
|
||||||
return airtaudio::errorNone;
|
return airtaudio::errorNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
void airtaudio::api::CoreIos::callBackEvent(void* _data,
|
void airtaudio::api::CoreIos::callBackEvent(void* _data,
|
||||||
int32_t _frameRate) {
|
int32_t _frameRate) {
|
||||||
/*
|
|
||||||
|
#if 0
|
||||||
|
static double value=0;
|
||||||
|
int16_t* vals = (int16_t*)_data;
|
||||||
|
for (int32_t iii=0; iii<_frameRate; ++iii) {
|
||||||
|
*vals++ = (int16_t)(sin(value) * 32760.0);
|
||||||
|
*vals++ = (int16_t)(sin(value) * 32760.0);
|
||||||
|
value += 0.09;
|
||||||
|
if (value >= M_PI*2.0) {
|
||||||
|
value -= M_PI*2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
int32_t doStopStream = 0;
|
int32_t doStopStream = 0;
|
||||||
airtaudio::AirTAudioCallback callback = (airtaudio::AirTAudioCallback) m_stream.callbackInfo.callback;
|
airtaudio::AirTAudioCallback callback = (airtaudio::AirTAudioCallback) m_stream.callbackInfo.callback;
|
||||||
double streamTime = getStreamTime();
|
double streamTime = getStreamTime();
|
||||||
@ -424,21 +148,29 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
airtaudio::Api::tickStreamTime();
|
airtaudio::Api::tickStreamTime();
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void airtaudio::api::CoreIos::androidCallBackEvent(void* _data,
|
static OSStatus playbackCallback(void *_userData,
|
||||||
int32_t _frameRate,
|
AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
void* _userData) {
|
const AudioTimeStamp *inTimeStamp,
|
||||||
/*
|
uint32_t inBusNumber,
|
||||||
|
uint32_t inNumberFrames,
|
||||||
|
AudioBufferList *ioData) {
|
||||||
if (_userData == NULL) {
|
if (_userData == NULL) {
|
||||||
ATA_INFO("callback event ... NULL pointer");
|
ATA_ERROR("callback event ... NULL pointer");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
airtaudio::api::Android* myClass = static_cast<airtaudio::api::Android*>(_userData);
|
airtaudio::api::CoreIos* myClass = static_cast<airtaudio::api::CoreIos*>(_userData);
|
||||||
myClass->callBackEvent(_data, _frameRate/2);
|
// get all requested buffer :
|
||||||
*/
|
for (int32_t iii=0; iii < ioData->mNumberBuffers; iii++) {
|
||||||
|
AudioBuffer buffer = ioData->mBuffers[iii];
|
||||||
|
int32_t numberFrame = buffer.mDataByteSize/2/*stereo*/ /sizeof(int16_t);
|
||||||
|
ATA_VERBOSE("request data size: " << numberFrame << " busNumber=" << inBusNumber);
|
||||||
|
myClass->callBackEvent(buffer.mData, numberFrame);
|
||||||
}
|
}
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device,
|
bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device,
|
||||||
airtaudio::api::StreamMode _mode,
|
airtaudio::api::StreamMode _mode,
|
||||||
@ -454,22 +186,15 @@ bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
iosAudio = [[IosAudioController alloc] init];
|
|
||||||
[iosAudio start];
|
// configure Airtaudio internal configuration:
|
||||||
/*
|
|
||||||
m_stream.userFormat = _format;
|
m_stream.userFormat = _format;
|
||||||
m_stream.nUserChannels[_mode] = _channels;
|
m_stream.nUserChannels[_mode] = _channels;
|
||||||
ewol::Context& tmpContext = ewol::getContext();
|
m_stream.bufferSize = 8192;
|
||||||
if (_format == SINT8) {
|
|
||||||
ret = tmpContext.audioOpenDevice(_device, _sampleRate, _channels, 0, androidCallBackEvent, this);
|
|
||||||
} else {
|
|
||||||
ret = tmpContext.audioOpenDevice(_device, _sampleRate, _channels, 1, androidCallBackEvent, this);
|
|
||||||
}
|
|
||||||
m_stream.bufferSize = 256;
|
|
||||||
m_stream.sampleRate = _sampleRate;
|
m_stream.sampleRate = _sampleRate;
|
||||||
m_stream.doByteSwap[_mode] = false; // for endienness ...
|
m_stream.doByteSwap[_mode] = false; // for endienness ...
|
||||||
|
|
||||||
// TODO : For now, we write it in hard ==> to bu update later ...
|
// TODO : For now, we write it in hard ==> to be update later ...
|
||||||
m_stream.deviceFormat[_mode] = SINT16;
|
m_stream.deviceFormat[_mode] = SINT16;
|
||||||
m_stream.nDeviceChannels[_mode] = 2;
|
m_stream.nDeviceChannels[_mode] = 2;
|
||||||
m_stream.deviceInterleaved[_mode] = true;
|
m_stream.deviceInterleaved[_mode] = true;
|
||||||
@ -500,7 +225,81 @@ bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device,
|
|||||||
if (ret == false) {
|
if (ret == false) {
|
||||||
ATA_ERROR("Can not open device.");
|
ATA_ERROR("Can not open device.");
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
// Configure IOs interface:
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
// Describe audio component
|
||||||
|
AudioComponentDescription desc;
|
||||||
|
desc.componentType = kAudioUnitType_Output;
|
||||||
|
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||||
|
desc.componentFlags = 0;
|
||||||
|
desc.componentFlagsMask = 0;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
|
||||||
|
// Get component
|
||||||
|
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
|
||||||
|
|
||||||
|
// Get audio units
|
||||||
|
status = AudioComponentInstanceNew(inputComponent, &m_private->audioUnit);
|
||||||
|
if (status != 0) {
|
||||||
|
ATA_ERROR("can not create an audio intance...");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t flag = 1;
|
||||||
|
// Enable IO for playback
|
||||||
|
status = AudioUnitSetProperty(m_private->audioUnit,
|
||||||
|
kAudioOutputUnitProperty_EnableIO,
|
||||||
|
kAudioUnitScope_Output,
|
||||||
|
kOutputBus,
|
||||||
|
&flag,
|
||||||
|
sizeof(flag));
|
||||||
|
if (status != 0) {
|
||||||
|
ATA_ERROR("can not request audio autorisation...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe format
|
||||||
|
AudioStreamBasicDescription audioFormat;
|
||||||
|
audioFormat.mSampleRate = 48000.00;
|
||||||
|
audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||||
|
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||||
|
audioFormat.mFramesPerPacket = 1; //
|
||||||
|
audioFormat.mChannelsPerFrame = 2; // stereo
|
||||||
|
audioFormat.mBitsPerChannel = sizeof(short) * 8;
|
||||||
|
audioFormat.mBytesPerPacket = sizeof(short) * audioFormat.mChannelsPerFrame;
|
||||||
|
audioFormat.mBytesPerFrame = sizeof(short) * audioFormat.mChannelsPerFrame;
|
||||||
|
audioFormat.mReserved = 0;
|
||||||
|
// Apply format
|
||||||
|
status = AudioUnitSetProperty(m_private->audioUnit,
|
||||||
|
kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Input,
|
||||||
|
kOutputBus,
|
||||||
|
&audioFormat,
|
||||||
|
sizeof(audioFormat));
|
||||||
|
if (status != 0) {
|
||||||
|
ATA_ERROR("can not set stream properties...");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set output callback
|
||||||
|
AURenderCallbackStruct callbackStruct;
|
||||||
|
callbackStruct.inputProc = playbackCallback;
|
||||||
|
callbackStruct.inputProcRefCon = this;
|
||||||
|
status = AudioUnitSetProperty(m_private->audioUnit,
|
||||||
|
kAudioUnitProperty_SetRenderCallback,
|
||||||
|
kAudioUnitScope_Global,
|
||||||
|
kOutputBus,
|
||||||
|
&callbackStruct,
|
||||||
|
sizeof(callbackStruct));
|
||||||
|
if (status != 0) {
|
||||||
|
ATA_ERROR("can not set Callback...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise
|
||||||
|
status = AudioUnitInitialize(m_private->audioUnit);
|
||||||
|
if (status != 0) {
|
||||||
|
ATA_ERROR("can not initialize...");
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user