/** * @author Gary P. SCAVONE * * @copyright 2001-2013 Gary P. Scavone, all right reserved * * @license like MIT (see license file) */ //#include #include #include #include #include #include #include std::ostream& operator <<(std::ostream& _os, const airtaudio::api::type& _obj){ switch (_obj) { default: case airtaudio::api::UNSPECIFIED: _os << "UNSPECIFIED"; break; case airtaudio::api::LINUX_ALSA: _os << "LINUX_ALSA"; break; case airtaudio::api::LINUX_PULSE: _os << "LINUX_PULSE"; break; case airtaudio::api::LINUX_OSS: _os << "LINUX_OSS"; break; case airtaudio::api::UNIX_JACK: _os << "UNIX_JACK"; break; case airtaudio::api::MACOSX_CORE: _os << "MACOSX_CORE"; break; case airtaudio::api::IOS_CORE: _os << "IOS_CORE"; break; case airtaudio::api::WINDOWS_ASIO: _os << "WINDOWS_ASIO"; break; case airtaudio::api::WINDOWS_DS: _os << "WINDOWS_DS"; break; case airtaudio::api::RTAUDIO_DUMMY: _os << "RTAUDIO_DUMMY"; break; case airtaudio::api::ANDROID_JAVA: _os << "ANDROID_JAVA"; break; case airtaudio::api::USER_INTERFACE_1: _os << "USER_INTERFACE_1"; break; case airtaudio::api::USER_INTERFACE_2: _os << "USER_INTERFACE_2"; break; case airtaudio::api::USER_INTERFACE_3: _os << "USER_INTERFACE_3"; break; case airtaudio::api::USER_INTERFACE_4: _os << "USER_INTERFACE_4"; break; } return _os; } // Static variable definitions. const uint32_t airtaudio::api::MAX_SAMPLE_RATES = 14; const uint32_t airtaudio::api::SAMPLE_RATES[] = { 4000, 5512, 8000, 9600, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; airtaudio::Api::Api() { m_stream.state = airtaudio::api::STREAM_CLOSED; m_stream.mode = airtaudio::api::UNINITIALIZED; m_stream.apiHandle = 0; m_stream.userBuffer[0] = 0; m_stream.userBuffer[1] = 0; } airtaudio::Api::~Api() { } enum airtaudio::errorType airtaudio::Api::openStream(airtaudio::StreamParameters *oParams, airtaudio::StreamParameters *iParams, airtaudio::format format, uint32_t sampleRate, uint32_t *bufferFrames, airtaudio::AirTAudioCallback callback, airtaudio::StreamOptions *options) { if (m_stream.state != airtaudio::api::STREAM_CLOSED) { ATA_ERROR("airtaudio::Api::openStream: a stream is already open!"); return airtaudio::errorInvalidUse; } if (oParams && oParams->nChannels < 1) { ATA_ERROR("airtaudio::Api::openStream: a non-nullptr output StreamParameters structure cannot have an nChannels value less than one."); return airtaudio::errorInvalidUse; } if (iParams && iParams->nChannels < 1) { ATA_ERROR("airtaudio::Api::openStream: a non-nullptr input StreamParameters structure cannot have an nChannels value less than one."); return airtaudio::errorInvalidUse; } if (oParams == nullptr && iParams == nullptr) { ATA_ERROR("airtaudio::Api::openStream: input and output StreamParameters structures are both nullptr!"); return airtaudio::errorInvalidUse; } if (formatBytes(format) == 0) { ATA_ERROR("airtaudio::Api::openStream: 'format' parameter value is undefined."); return airtaudio::errorInvalidUse; } uint32_t nDevices = getDeviceCount(); uint32_t oChannels = 0; if (oParams) { oChannels = oParams->nChannels; if (oParams->deviceId >= nDevices) { ATA_ERROR("airtaudio::Api::openStream: output device parameter value is invalid."); return airtaudio::errorInvalidUse; } } uint32_t iChannels = 0; if (iParams) { iChannels = iParams->nChannels; if (iParams->deviceId >= nDevices) { ATA_ERROR("airtaudio::Api::openStream: input device parameter value is invalid."); return airtaudio::errorInvalidUse; } } clearStreamInfo(); bool result; if (oChannels > 0) { result = probeDeviceOpen(oParams->deviceId, airtaudio::api::OUTPUT, oChannels, oParams->firstChannel, sampleRate, format, bufferFrames, options); if (result == false) { ATA_ERROR("system ERROR"); return airtaudio::errorSystemError; } } if (iChannels > 0) { result = probeDeviceOpen(iParams->deviceId, airtaudio::api::INPUT, iChannels, iParams->firstChannel, sampleRate, format, bufferFrames, options); if (result == false) { if (oChannels > 0) { closeStream(); } ATA_ERROR("system error"); return airtaudio::errorSystemError; } } m_stream.callbackInfo.callback = callback; if (options != nullptr) { options->numberOfBuffers = m_stream.nBuffers; } m_stream.state = airtaudio::api::STREAM_STOPPED; return airtaudio::errorNone; } uint32_t airtaudio::Api::getDefaultInputDevice() { // Should be implemented in subclasses if possible. return 0; } uint32_t airtaudio::Api::getDefaultOutputDevice() { // Should be implemented in subclasses if possible. return 0; } enum airtaudio::errorType airtaudio::Api::closeStream() { // MUST be implemented in subclasses! return airtaudio::errorNone; } bool airtaudio::Api::probeDeviceOpen(uint32_t /*device*/, airtaudio::api::StreamMode /*mode*/, uint32_t /*channels*/, uint32_t /*firstChannel*/, uint32_t /*sampleRate*/, airtaudio::format /*format*/, uint32_t * /*bufferSize*/, airtaudio::StreamOptions * /*options*/) { // MUST be implemented in subclasses! return false; } void airtaudio::Api::tickStreamTime() { // Subclasses that do not provide their own implementation of // getStreamTime should call this function once per buffer I/O to // provide basic stream time support. m_stream.streamTime += (m_stream.bufferSize * 1.0 / m_stream.sampleRate); #if defined(HAVE_GETTIMEOFDAY) gettimeofday(&m_stream.lastTickTimestamp, nullptr); #endif } long airtaudio::Api::getStreamLatency() { if (verifyStream() != airtaudio::errorNone) { return 0; } long totalLatency = 0; if ( m_stream.mode == airtaudio::api::OUTPUT || m_stream.mode == airtaudio::api::DUPLEX) { totalLatency = m_stream.latency[0]; } if ( m_stream.mode == airtaudio::api::INPUT || m_stream.mode == airtaudio::api::DUPLEX) { totalLatency += m_stream.latency[1]; } return totalLatency; } double airtaudio::Api::getStreamTime() { if (verifyStream() != airtaudio::errorNone) { return 0.0f; } #if defined(HAVE_GETTIMEOFDAY) // Return a very accurate estimate of the stream time by // adding in the elapsed time since the last tick. struct timeval then; struct timeval now; if (m_stream.state != airtaudio::api::STREAM_RUNNING || m_stream.streamTime == 0.0) { return m_stream.streamTime; } gettimeofday(&now, nullptr); then = m_stream.lastTickTimestamp; return m_stream.streamTime + ((now.tv_sec + 0.000001 * now.tv_usec) - (then.tv_sec + 0.000001 * then.tv_usec)); #else return m_stream.streamTime; #endif } uint32_t airtaudio::Api::getStreamSampleRate() { if (verifyStream() != airtaudio::errorNone) { return 0; } return m_stream.sampleRate; } enum airtaudio::errorType airtaudio::Api::verifyStream() { if (m_stream.state == airtaudio::api::STREAM_CLOSED) { ATA_ERROR("airtaudio::Api:: a stream is not open!"); return airtaudio::errorInvalidUse; } return airtaudio::errorNone; } void airtaudio::Api::clearStreamInfo() { m_stream.mode = airtaudio::api::UNINITIALIZED; m_stream.state = airtaudio::api::STREAM_CLOSED; m_stream.sampleRate = 0; m_stream.bufferSize = 0; m_stream.nBuffers = 0; m_stream.userFormat = 0; m_stream.userInterleaved = true; m_stream.streamTime = 0.0; m_stream.apiHandle = 0; m_stream.deviceBuffer = 0; m_stream.callbackInfo.callback = 0; m_stream.callbackInfo.isRunning = false; for (int32_t iii=0; iii<2; ++iii) { m_stream.device[iii] = 11111; m_stream.doConvertBuffer[iii] = false; m_stream.deviceInterleaved[iii] = true; m_stream.doByteSwap[iii] = false; m_stream.nUserChannels[iii] = 0; m_stream.nDeviceChannels[iii] = 0; m_stream.channelOffset[iii] = 0; m_stream.deviceFormat[iii] = 0; m_stream.latency[iii] = 0; m_stream.userBuffer[iii] = 0; m_stream.convertInfo[iii].channels = 0; m_stream.convertInfo[iii].inJump = 0; m_stream.convertInfo[iii].outJump = 0; m_stream.convertInfo[iii].inFormat = 0; m_stream.convertInfo[iii].outFormat = 0; m_stream.convertInfo[iii].inOffset.clear(); m_stream.convertInfo[iii].outOffset.clear(); } } uint32_t airtaudio::Api::formatBytes(airtaudio::format _format) { if (_format == airtaudio::SINT16) { return 2; } else if ( _format == airtaudio::SINT32 || _format == airtaudio::FLOAT32) { return 4; } else if (_format == airtaudio::FLOAT64) { return 8; } else if (_format == airtaudio::SINT24) { return 3; } else if (_format == airtaudio::SINT8) { return 1; } ATA_ERROR("airtaudio::Api::formatBytes: undefined format."); // TODO : airtaudio::errorWarning; return 0; } void airtaudio::Api::setConvertInfo(airtaudio::api::StreamMode _mode, uint32_t _firstChannel) { if (_mode == airtaudio::api::INPUT) { // convert device to user buffer m_stream.convertInfo[_mode].inJump = m_stream.nDeviceChannels[1]; m_stream.convertInfo[_mode].outJump = m_stream.nUserChannels[1]; m_stream.convertInfo[_mode].inFormat = m_stream.deviceFormat[1]; m_stream.convertInfo[_mode].outFormat = m_stream.userFormat; } else { // convert user to device buffer m_stream.convertInfo[_mode].inJump = m_stream.nUserChannels[0]; m_stream.convertInfo[_mode].outJump = m_stream.nDeviceChannels[0]; m_stream.convertInfo[_mode].inFormat = m_stream.userFormat; m_stream.convertInfo[_mode].outFormat = m_stream.deviceFormat[0]; } if (m_stream.convertInfo[_mode].inJump < m_stream.convertInfo[_mode].outJump) { m_stream.convertInfo[_mode].channels = m_stream.convertInfo[_mode].inJump; } else { m_stream.convertInfo[_mode].channels = m_stream.convertInfo[_mode].outJump; } // Set up the interleave/deinterleave offsets. if (m_stream.deviceInterleaved[_mode] != m_stream.userInterleaved) { if ( ( _mode == airtaudio::api::OUTPUT && m_stream.deviceInterleaved[_mode]) || ( _mode == airtaudio::api::INPUT && m_stream.userInterleaved)) { for (int32_t kkk=0; kkk 0) { if (m_stream.deviceInterleaved[_mode]) { if (_mode == airtaudio::api::OUTPUT) { for (int32_t kkk=0; kkk> 8); } in += _info.inJump; out += _info.outJump; } } else if (_info.inFormat == airtaudio::FLOAT32) { float *in = (float *)_inBuffer; for (uint32_t iii=0; iii> 8); } in += _info.inJump; out += _info.outJump; } } else if (_info.inFormat == airtaudio::SINT32) { int32_t *in = (int32_t *)_inBuffer; for (uint32_t iii=0; iii> 16) & 0x0000ffff); } in += _info.inJump; out += _info.outJump; } } else if (_info.inFormat == airtaudio::FLOAT32) { float *in = (float *)_inBuffer; for (uint32_t iii=0; iii> 8) & 0x00ff); } in += _info.inJump; out += _info.outJump; } } else if (_info.inFormat == airtaudio::SINT24) { int24_t *in = (int24_t *)_inBuffer; for (uint32_t iii=0; iii> 16); } in += _info.inJump; out += _info.outJump; } } else if (_info.inFormat == airtaudio::SINT32) { int32_t *in = (int32_t *)_inBuffer; for (uint32_t iii=0; iii> 24) & 0x000000ff); } in += _info.inJump; out += _info.outJump; } } else if (_info.inFormat == airtaudio::FLOAT32) { float *in = (float *)_inBuffer; for (uint32_t iii=0; iii