audio-orchestra/airtaudio/api/CoreIos.mm

310 lines
10 KiB
Plaintext
Raw Normal View History

2015-02-09 21:44:32 +01:00
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
* @fork from RTAudio
2014-05-09 09:14:22 +02:00
*/
#ifdef __IOS_CORE__
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#include <unistd.h>
#include <airtaudio/Interface.h>
#include <airtaudio/debug.h>
#include <limits.h>
2015-01-27 23:06:19 +01:00
#undef __class__
#define __class__ "api::CoreIos"
2014-05-09 09:14:22 +02:00
airtaudio::Api* airtaudio::api::CoreIos::Create(void) {
ATA_INFO("Create CoreIos device ... ");
return new airtaudio::api::CoreIos();
}
#define kOutputBus 0
#define kInputBus 1
namespace airtaudio {
namespace api {
class CoreIosPrivate {
2014-05-09 14:47:18 +02:00
public:
AudioComponentInstance audioUnit;
2014-05-09 09:14:22 +02:00
};
};
};
2014-05-09 14:47:18 +02:00
2014-05-09 09:14:22 +02:00
airtaudio::api::CoreIos::CoreIos(void) :
m_private(new airtaudio::api::CoreIosPrivate()) {
2014-05-09 09:14:22 +02:00
ATA_INFO("new CoreIos");
int32_t deviceCount = 2;
ATA_ERROR("Get count devices : " << 2);
airtaudio::DeviceInfo tmp;
// Add default output format :
tmp.name = "out";
tmp.sampleRates.push_back(48000);
tmp.outputChannels = 2;
tmp.inputChannels = 0;
tmp.duplexChannels = 0;
tmp.isDefaultOutput = true;
tmp.isDefaultInput = false;
tmp.nativeFormats.push_back(audio::format_int16);
2014-05-09 09:14:22 +02:00
m_devices.push_back(tmp);
// add default input format:
tmp.name = "in";
tmp.sampleRates.push_back(48000);
tmp.outputChannels = 0;
tmp.inputChannels = 2;
tmp.duplexChannels = 0;
tmp.isDefaultOutput = false;
tmp.isDefaultInput = true;
tmp.nativeFormats.push_back(audio::format_int16);
2014-05-09 09:14:22 +02:00
m_devices.push_back(tmp);
ATA_INFO("Create CoreIOs interface (end)");
}
airtaudio::api::CoreIos::~CoreIos(void) {
ATA_INFO("Destroy CoreIOs interface");
2014-05-09 14:47:18 +02:00
AudioUnitUninitialize(m_private->audioUnit);
2014-05-09 09:14:22 +02:00
delete m_private;
m_private = nullptr;
2014-05-09 09:14:22 +02:00
}
uint32_t airtaudio::api::CoreIos::getDeviceCount(void) {
//ATA_INFO("Get device count:"<< m_devices.size());
return m_devices.size();
}
airtaudio::DeviceInfo airtaudio::api::CoreIos::getDeviceInfo(uint32_t _device) {
//ATA_INFO("Get device info ...");
return m_devices[_device];
}
2015-02-06 23:54:08 +01:00
enum airtaudio::error airtaudio::api::CoreIos::closeStream(void) {
2014-05-09 09:14:22 +02:00
ATA_INFO("Close Stream");
// Can not close the stream now...
2015-02-06 23:54:08 +01:00
return airtaudio::error_none;
2014-05-09 09:14:22 +02:00
}
2015-02-06 23:54:08 +01:00
enum airtaudio::error airtaudio::api::CoreIos::startStream(void) {
2014-05-09 09:14:22 +02:00
ATA_INFO("Start Stream");
2015-02-10 22:38:30 +01:00
// TODO : Check return ...
airtaudio::Api::startStream();
2014-05-09 14:47:18 +02:00
OSStatus status = AudioOutputUnitStart(m_private->audioUnit);
2014-05-09 09:14:22 +02:00
// Can not close the stream now...
2015-02-06 23:54:08 +01:00
return airtaudio::error_none;
2014-05-09 09:14:22 +02:00
}
2015-02-06 23:54:08 +01:00
enum airtaudio::error airtaudio::api::CoreIos::stopStream(void) {
2014-05-09 09:14:22 +02:00
ATA_INFO("Stop stream");
2014-05-09 14:47:18 +02:00
OSStatus status = AudioOutputUnitStop(m_private->audioUnit);
2014-05-09 09:14:22 +02:00
// Can not close the stream now...
2015-02-06 23:54:08 +01:00
return airtaudio::error_none;
2014-05-09 09:14:22 +02:00
}
2015-02-06 23:54:08 +01:00
enum airtaudio::error airtaudio::api::CoreIos::abortStream(void) {
2014-05-09 09:14:22 +02:00
ATA_INFO("Abort Stream");
2014-05-09 14:47:18 +02:00
OSStatus status = AudioOutputUnitStop(m_private->audioUnit);
2014-05-09 09:14:22 +02:00
// Can not close the stream now...
2015-02-06 23:54:08 +01:00
return airtaudio::error_none;
2014-05-09 09:14:22 +02:00
}
void airtaudio::api::CoreIos::callBackEvent(void* _data,
int32_t _frameRate) {
2014-05-09 14:47:18 +02:00
#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
2014-05-09 09:14:22 +02:00
int32_t doStopStream = 0;
2015-02-10 22:38:30 +01:00
std::chrono::system_clock::time_point streamTime = getStreamTime();
2015-02-06 23:54:08 +01:00
enum airtaudio::status status = airtaudio::status_ok;
2015-02-09 21:44:32 +01:00
if (m_doConvertBuffer[airtaudio::mode_output] == true) {
doStopStream = m_callbackInfo.callback(m_userBuffer[airtaudio::mode_output],
nullptr,
_frameRate,
streamTime,
status);
2015-02-09 21:44:32 +01:00
convertBuffer((char*)_data, (char*)m_userBuffer[airtaudio::mode_output], m_convertInfo[airtaudio::mode_output]);
2014-05-09 09:14:22 +02:00
} else {
2015-02-09 21:44:32 +01:00
doStopStream = m_callbackInfo.callback(_data,
nullptr,
_frameRate,
streamTime,
status);
2014-05-09 09:14:22 +02:00
}
if (doStopStream == 2) {
abortStream();
return;
}
airtaudio::Api::tickStreamTime();
}
2015-02-09 21:44:32 +01:00
OSStatus airtaudio::api::CoreIos::playbackCallback(void *_userData,
AudioUnitRenderActionFlags* _ioActionFlags,
const AudioTimeStamp* _inTimeStamp,
uint32_t _inBusNumber,
uint32_t _inNumberFrames,
AudioBufferList* _ioData) {
if (_userData == nullptr) {
ATA_ERROR("callback event ... nullptr pointer");
2014-05-09 14:47:18 +02:00
return -1;
2014-05-09 09:14:22 +02:00
}
2014-05-09 14:47:18 +02:00
airtaudio::api::CoreIos* myClass = static_cast<airtaudio::api::CoreIos*>(_userData);
// get all requested buffer :
2015-02-09 21:44:32 +01:00
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);
2014-05-09 14:47:18 +02:00
myClass->callBackEvent(buffer.mData, numberFrame);
}
2015-02-09 21:44:32 +01:00
return noErr;
2014-05-09 09:14:22 +02:00
}
2014-05-09 14:47:18 +02:00
2014-05-09 09:14:22 +02:00
bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device,
2015-02-06 23:54:08 +01:00
airtaudio::mode _mode,
2014-05-09 09:14:22 +02:00
uint32_t _channels,
uint32_t _firstChannel,
uint32_t _sampleRate,
audio::format _format,
2014-05-09 09:14:22 +02:00
uint32_t *_bufferSize,
airtaudio::StreamOptions *_options) {
ATA_INFO("Probe : device=" << _device << " channels=" << _channels << " firstChannel=" << _firstChannel << " sampleRate=" << _sampleRate);
2015-02-06 23:54:08 +01:00
if (_mode != airtaudio::mode_output) {
2014-05-09 09:14:22 +02:00
ATA_ERROR("Can not start a device input or duplex for CoreIos ...");
return false;
}
bool ret = true;
2014-05-09 14:47:18 +02:00
// configure Airtaudio internal configuration:
2015-02-09 21:44:32 +01:00
m_userFormat = _format;
m_nUserChannels[modeToIdTable(_mode)] = _channels;
m_bufferSize = 8192;
m_sampleRate = _sampleRate;
m_doByteSwap[modeToIdTable(_mode)] = false; // for endienness ...
2014-05-09 09:14:22 +02:00
2014-05-09 14:47:18 +02:00
// TODO : For now, we write it in hard ==> to be update later ...
2015-02-09 21:44:32 +01:00
m_deviceFormat[modeToIdTable(_mode)] = SINT16;
m_nDeviceChannels[modeToIdTable(_mode)] = 2;
m_deviceInterleaved[modeToIdTable(_mode)] = true;
2014-05-09 09:14:22 +02:00
2015-02-09 21:44:32 +01:00
m_doConvertBuffer[modeToIdTable(_mode)] = false;
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
m_doConvertBuffer[modeToIdTable(_mode)] = true;
2014-05-09 09:14:22 +02:00
}
2015-02-09 21:44:32 +01:00
if (m_nUserChannels[modeToIdTable(_mode)] < m_nDeviceChannels[modeToIdTable(_mode)]) {
m_doConvertBuffer[modeToIdTable(_mode)] = true;
2014-05-09 09:14:22 +02:00
}
2015-02-09 21:44:32 +01:00
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
m_doConvertBuffer[modeToIdTable(_mode)] = true;
2014-05-09 09:14:22 +02:00
}
2015-02-09 21:44:32 +01:00
if (m_doConvertBuffer[modeToIdTable(_mode)] == true) {
2014-05-09 09:14:22 +02:00
// Allocate necessary internal buffers.
2015-02-09 21:44:32 +01:00
uint64_t bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
if (m_userBuffer[modeToIdTable(_mode)] == nullptr) {
ATA_ERROR("error allocating user buffer memory.");
2014-05-09 09:14:22 +02:00
}
setConvertInfo(_mode, _firstChannel);
}
2015-02-09 21:44:32 +01:00
ATA_INFO("device format : " << m_deviceFormat[modeToIdTable(_mode)] << " user format : " << m_userFormat);
ATA_INFO("device channels : " << m_nDeviceChannels[modeToIdTable(_mode)] << " user channels : " << m_nUserChannels[modeToIdTable(_mode)]);
ATA_INFO("do convert buffer : " << m_doConvertBuffer[modeToIdTable(_mode)]);
2014-05-09 09:14:22 +02:00
if (ret == false) {
ATA_ERROR("Can not open device.");
}
2014-05-09 14:47:18 +02:00
// 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(nullptr, &desc);
2014-05-09 14:47:18 +02:00
// 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;
2015-02-09 21:44:32 +01:00
callbackStruct.inputProc = &airtaudio::api::CoreIos::playbackCallback;
2014-05-09 14:47:18 +02:00
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...");
}
2014-05-09 09:14:22 +02:00
return ret;
}
#endif