2014-02-24 14:39:10 -05:00
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
//
|
2014-04-30 13:46:41 -04:00
|
|
|
|
// Copyright (c) 2008 Waves Audio Ltd. All rights reserved.
|
2014-02-24 14:39:10 -05:00
|
|
|
|
//
|
|
|
|
|
//! \file WCMRNativeAudio.cpp
|
|
|
|
|
//!
|
|
|
|
|
//! WCMRNativeAudioConnection and related class defienitions
|
|
|
|
|
//!
|
|
|
|
|
//---------------------------------------------------------------------------------*/
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#if defined(__APPLE__)
|
2014-02-24 14:39:10 -05:00
|
|
|
|
#include <CoreAudio/CoreAudio.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "WCMRNativeAudio.h"
|
2014-09-09 07:05:14 -04:00
|
|
|
|
#include "MiscUtils/pthread_utils.h"
|
2014-02-24 14:39:10 -05:00
|
|
|
|
#include "MiscUtils/safe_delete.h"
|
2014-05-06 12:00:35 -04:00
|
|
|
|
#include <iostream>
|
2014-02-24 14:39:10 -05:00
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <boost/assign/list_of.hpp>
|
|
|
|
|
|
|
|
|
|
#define NONE_DEVICE_NAME "None"
|
|
|
|
|
#define NONE_DEVICE_INPUT_NAMES "Input "
|
|
|
|
|
#define NONE_DEVICE_OUTPUT_NAMES "Output "
|
|
|
|
|
|
|
|
|
|
//**********************************************************************************************
|
|
|
|
|
// WCMRNativeAudioNoneDevice::WCMRNativeAudioNoneDevice
|
|
|
|
|
//
|
|
|
|
|
//! Constructor for the dummy "None" device. This constructor simply adds supported SRs,
|
|
|
|
|
//! buffer sizes, and channels, so that it may look like a real native device to
|
|
|
|
|
//! the applications.
|
|
|
|
|
//!
|
|
|
|
|
//! \param pManager : The managing device manager - simply passed on to the base class.
|
2015-10-04 15:11:15 -04:00
|
|
|
|
//!
|
|
|
|
|
//!
|
2014-02-24 14:39:10 -05:00
|
|
|
|
//**********************************************************************************************
|
|
|
|
|
WCMRNativeAudioNoneDevice::WCMRNativeAudioNoneDevice (WCMRAudioDeviceManager *pManager)
|
|
|
|
|
: WCMRNativeAudioDevice (pManager, false /*useMultiThreading*/)
|
2015-06-30 03:11:05 -04:00
|
|
|
|
#ifndef PTW32_VERSION
|
2015-05-14 10:52:12 -04:00
|
|
|
|
, m_SilenceThread(0)
|
2015-06-30 03:11:05 -04:00
|
|
|
|
#endif
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#if defined (PLATFORM_WINDOWS)
|
2014-02-24 14:39:10 -05:00
|
|
|
|
, _waitableTimerForUsleep (CreateWaitableTimer(NULL, TRUE, NULL))
|
|
|
|
|
#endif
|
|
|
|
|
{
|
2014-09-09 07:05:14 -04:00
|
|
|
|
mark_pthread_inactive (m_SilenceThread);
|
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
m_DeviceName = NONE_DEVICE_NAME;
|
|
|
|
|
|
2014-09-30 20:35:31 -04:00
|
|
|
|
m_SamplingRates = boost::assign::list_of (m_CurrentSamplingRate=44100)(48000)(88200)(96000)(176400)(192000);
|
2014-02-24 14:39:10 -05:00
|
|
|
|
|
|
|
|
|
m_BufferSizes = boost::assign::list_of (32)(64)(128)(m_CurrentBufferSize=256)(512)(1024);
|
|
|
|
|
|
|
|
|
|
for (int channel = 0; channel < __m_NumInputChannels; channel++)
|
|
|
|
|
{
|
|
|
|
|
std::stringstream name;
|
|
|
|
|
name << NONE_DEVICE_INPUT_NAMES;
|
|
|
|
|
name << (channel + 1);
|
|
|
|
|
m_InputChannels.push_back(name.str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int channel = 0; channel < __m_NumOutputChannels; channel++)
|
|
|
|
|
{
|
|
|
|
|
std::stringstream name;
|
|
|
|
|
name << NONE_DEVICE_INPUT_NAMES;
|
|
|
|
|
name << (channel + 1);
|
|
|
|
|
m_OutputChannels.push_back(name.str());
|
|
|
|
|
}
|
|
|
|
|
_m_inputBuffer = new float[__m_NumInputChannels * m_BufferSizes.back()];
|
|
|
|
|
_m_outputBuffer = new float[__m_NumOutputChannels * m_BufferSizes.back()];
|
2015-01-19 11:50:50 -05:00
|
|
|
|
m_CurrentBufferSize = m_BufferSizes.back();
|
2014-02-24 14:39:10 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
WCMRNativeAudioNoneDevice::~WCMRNativeAudioNoneDevice ()
|
|
|
|
|
{
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#if defined (PLATFORM_WINDOWS)
|
2014-02-24 14:39:10 -05:00
|
|
|
|
if(_waitableTimerForUsleep) {
|
|
|
|
|
CloseHandle(_waitableTimerForUsleep);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WTErr WCMRNativeAudioNoneDevice::SetActive (bool newState)
|
|
|
|
|
{
|
|
|
|
|
//This will most likely be overridden, the base class simply
|
|
|
|
|
//changes the member.
|
|
|
|
|
if (Active() == newState)
|
|
|
|
|
{
|
|
|
|
|
return (eNoErr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Active() && Streaming())
|
|
|
|
|
{
|
|
|
|
|
SetStreaming(false);
|
|
|
|
|
}
|
|
|
|
|
return WCMRAudioDevice::SetActive(newState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WTErr WCMRNativeAudioNoneDevice::SetCurrentBufferSize (int newSize)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
//changes the status.
|
|
|
|
|
int oldSize = CurrentBufferSize();
|
|
|
|
|
bool oldActive = Active();
|
|
|
|
|
|
|
|
|
|
//same size, nothing to do.
|
|
|
|
|
if (oldSize == newSize)
|
|
|
|
|
return eNoErr;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
//see if this is one of our supported rates...
|
|
|
|
|
std::vector<int>::iterator intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), newSize);
|
|
|
|
|
if (intIter == m_BufferSizes.end())
|
|
|
|
|
{
|
|
|
|
|
//Can't change, perhaps use an "invalid param" type of error
|
|
|
|
|
return eCommandLineParameter;
|
|
|
|
|
}
|
2015-10-05 10:17:49 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
if (Streaming())
|
|
|
|
|
{
|
|
|
|
|
//Can't change, perhaps use an "in use" type of error
|
|
|
|
|
return eGenericErr;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-05 10:17:49 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
return WCMRAudioDevice::SetCurrentBufferSize(newSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-04-30 13:46:41 -04:00
|
|
|
|
WTErr WCMRNativeAudioNoneDevice::UpdateDeviceInfo ()
|
|
|
|
|
{
|
|
|
|
|
return eNoErr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
WTErr WCMRNativeAudioNoneDevice::SetStreaming (bool newState)
|
|
|
|
|
{
|
|
|
|
|
if (Streaming() == newState)
|
|
|
|
|
{
|
|
|
|
|
return (eNoErr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WCMRAudioDevice::SetStreaming(newState);
|
2014-04-30 13:46:41 -04:00
|
|
|
|
|
|
|
|
|
if (Streaming())
|
2014-02-24 14:39:10 -05:00
|
|
|
|
{
|
2014-09-09 07:05:14 -04:00
|
|
|
|
if (is_pthread_active (m_SilenceThread))
|
2014-02-24 14:39:10 -05:00
|
|
|
|
std::cerr << "\t\t\t\t\t !!!!!!!!!!!!!!! Warning: the inactive NONE-DEVICE was streaming!" << std::endl;
|
|
|
|
|
|
|
|
|
|
pthread_attr_t attributes;
|
|
|
|
|
size_t stack_size = 100000;
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#ifdef __APPLE__
|
2014-02-24 14:39:10 -05:00
|
|
|
|
stack_size = (((stack_size - 1) / PTHREAD_STACK_MIN) + 1) * PTHREAD_STACK_MIN;
|
|
|
|
|
#endif
|
|
|
|
|
if (pthread_attr_init (&attributes)) {
|
|
|
|
|
std::cerr << "WCMRNativeAudioNoneDevice::SetStreaming (): pthread_attr_init () failed!" << std::endl;
|
|
|
|
|
return eGenericErr;
|
|
|
|
|
}
|
2015-10-04 15:11:15 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
if (pthread_attr_setstacksize (&attributes, stack_size)) {
|
|
|
|
|
std::cerr << "WCMRNativeAudioNoneDevice::SetStreaming (): pthread_attr_setstacksize () failed!" << std::endl;
|
|
|
|
|
return eGenericErr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pthread_create (&m_SilenceThread, &attributes, __SilenceThread, this)) {
|
2014-09-09 07:05:14 -04:00
|
|
|
|
mark_pthread_inactive (m_SilenceThread);
|
2014-02-24 14:39:10 -05:00
|
|
|
|
std::cerr << "WCMRNativeAudioNoneDevice::SetStreaming (): pthread_create () failed!" << std::endl;
|
|
|
|
|
return eGenericErr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-09-09 07:05:14 -04:00
|
|
|
|
if (!is_pthread_active (m_SilenceThread))
|
2014-02-24 14:39:10 -05:00
|
|
|
|
{
|
|
|
|
|
std::cerr << "\t\t\t\t\t !!!!!!!!!!!!!!! Warning: the active NONE-DEVICE was NOT streaming!" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 07:05:14 -04:00
|
|
|
|
while (is_pthread_active (m_SilenceThread))
|
2014-02-24 14:39:10 -05:00
|
|
|
|
{
|
|
|
|
|
_usleep(1); //now wait for ended thread;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return eNoErr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WCMRNativeAudioNoneDevice::_SilenceThread()
|
|
|
|
|
{
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#if defined(PLATFORM_WINDOWS)
|
2014-02-24 14:39:10 -05:00
|
|
|
|
float* theInpBuffers[__m_NumInputChannels];
|
|
|
|
|
for(int i = 0; i < __m_NumInputChannels; ++i)
|
|
|
|
|
{
|
|
|
|
|
theInpBuffers[i] = _m_inputBuffer + m_BufferSizes.back() * i;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
float* theInpBuffers = _m_inputBuffer;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
const size_t buffer_size = CurrentBufferSize();
|
|
|
|
|
const uint64_t cyclePeriodNanos = (1000000000.0 * buffer_size) / CurrentSamplingRate();
|
|
|
|
|
|
|
|
|
|
struct WCMRAudioDeviceManagerClient::AudioCallbackData audioCallbackData =
|
|
|
|
|
{
|
|
|
|
|
(const float*)theInpBuffers,
|
|
|
|
|
_m_outputBuffer,
|
|
|
|
|
buffer_size,
|
2015-10-04 15:11:15 -04:00
|
|
|
|
0,
|
2014-02-24 14:39:10 -05:00
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
audioCallbackData.acdCycleStartTimeNanos =__get_time_nanos();
|
|
|
|
|
|
2015-10-04 15:11:15 -04:00
|
|
|
|
// VERY ROUGH IMPLEMENTATION:
|
2014-02-24 14:39:10 -05:00
|
|
|
|
while(Streaming()) {
|
2015-10-05 10:17:49 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
uint64_t cycleEndTimeNanos = audioCallbackData.acdCycleStartTimeNanos + cyclePeriodNanos;
|
|
|
|
|
|
|
|
|
|
m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::AudioCallback, (void *)&audioCallbackData);
|
2015-10-05 10:17:49 -04:00
|
|
|
|
|
2015-02-20 05:52:47 -05:00
|
|
|
|
audioCallbackData.acdSampleTime += buffer_size;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
int64_t timeToSleepUsecs = ((int64_t)cycleEndTimeNanos - (int64_t)__get_time_nanos())/1000;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
if (timeToSleepUsecs > 0) {
|
|
|
|
|
_usleep (timeToSleepUsecs);
|
|
|
|
|
}
|
|
|
|
|
audioCallbackData.acdCycleStartTimeNanos = cycleEndTimeNanos+1;
|
|
|
|
|
}
|
2014-09-09 07:05:14 -04:00
|
|
|
|
mark_pthread_inactive (m_SilenceThread);
|
2014-02-24 14:39:10 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* WCMRNativeAudioNoneDevice::__SilenceThread(void *This)
|
|
|
|
|
{
|
|
|
|
|
((WCMRNativeAudioNoneDevice*)This)->_SilenceThread();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#if defined(PLATFORM_WINDOWS)
|
2014-02-24 14:39:10 -05:00
|
|
|
|
void WCMRNativeAudioNoneDevice::_usleep(uint64_t duration_usec)
|
2015-10-04 15:11:15 -04:00
|
|
|
|
{
|
|
|
|
|
LARGE_INTEGER ft;
|
2014-02-24 14:39:10 -05:00
|
|
|
|
|
|
|
|
|
ft.QuadPart = -(10*duration_usec); // Convert to 100 nanosecond interval, negative value indicates relative time
|
|
|
|
|
|
|
|
|
|
SetWaitableTimer(_waitableTimerForUsleep, &ft, 0, NULL, NULL, 0);
|
2015-10-04 15:11:15 -04:00
|
|
|
|
WaitForSingleObject(_waitableTimerForUsleep, INFINITE);
|
2014-02-24 14:39:10 -05:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
uint64_t
|
|
|
|
|
WCMRNativeAudioNoneDevice::__get_time_nanos ()
|
|
|
|
|
{
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#ifdef __APPLE__
|
2014-02-24 14:39:10 -05:00
|
|
|
|
// here we exploit the time counting API which is used by the WCMRCoreAudioDeviceManager. However,
|
|
|
|
|
// the API should be a part of WCMRCoreAudioDeviceManager to give a chance of being tied to the
|
|
|
|
|
// audio device transport time<6D>.
|
|
|
|
|
return AudioConvertHostTimeToNanos (AudioGetCurrentHostTime ());
|
2015-10-04 15:11:15 -04:00
|
|
|
|
|
2014-05-01 09:39:11 -04:00
|
|
|
|
#elif PLATFORM_WINDOWS
|
2015-10-04 15:11:15 -04:00
|
|
|
|
|
2014-02-24 14:39:10 -05:00
|
|
|
|
LARGE_INTEGER Frequency, Count ;
|
|
|
|
|
|
|
|
|
|
QueryPerformanceFrequency (&Frequency) ;
|
|
|
|
|
QueryPerformanceCounter (&Count);
|
|
|
|
|
return uint64_t ((Count.QuadPart * 1000000000.0 / Frequency.QuadPart));
|
|
|
|
|
#endif
|
|
|
|
|
}
|