#include "Audacity.h"
#include "float_cast.h"
#include <math.h>
#include <stdlib.h>
#ifdef __WXMSW__
#include <malloc.h>
#endif
#include <wx/log.h>
#include <wx/textctrl.h>
#include <wx/msgdlg.h>
#include <wx/timer.h>
#include <wx/intl.h>
#include <wx/debug.h>
#include "AudacityApp.h"
#include "AudioIO.h"
#include "WaveTrack.h"
#include "Mix.h"
#include "Resample.h"
#include "RingBuffer.h"
#include "Prefs.h"
#include "TimeTrack.h"
#include "widgets/Meter.h"
#define NO_STABLE_INDICATOR -1000000000
AudioIO *gAudioIO;
int AudioIO::mNextStreamToken = 0;
const int AudioIO::StandardRates[] = {
8000,
16000,
22050,
44100,
48000,
96000
};
const int AudioIO::NumStandardRates = sizeof(AudioIO::StandardRates) /
sizeof(AudioIO::StandardRates[0]);
#if USE_PORTAUDIO_V19
int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData );
#else
int audacityAudioCallback(void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
#endif
#ifdef __WXMAC__
#include <pthread.h>
#include <time.h>
class AudioThread {
public:
typedef int ExitCode;
AudioThread() { mDestroy = false; mThread = NULL; }
ExitCode Entry();
void Create() {}
void Delete() {
mDestroy = true;
pthread_join(mThread, NULL);
}
bool TestDestroy() { return mDestroy; }
void Sleep(int ms) {
struct timespec spec;
spec.tv_sec = 0;
spec.tv_nsec = ms * 1000 * 1000;
nanosleep(&spec, NULL);
}
static void *callback(void *p) {
AudioThread *th = (AudioThread *)p;
return (void *)th->Entry();
}
void Run() {
pthread_create(&mThread, NULL, callback, this);
}
private:
bool mDestroy;
pthread_t mThread;
};
#else
00147 class AudioThread : public wxThread {
public:
AudioThread():wxThread(wxTHREAD_JOINABLE) {}
virtual ExitCode Entry();
};
#endif
void InitAudioIO()
{
gAudioIO = new AudioIO();
gAudioIO->mThread->Run();
}
void DeinitAudioIO()
{
delete gAudioIO;
}
wxString DeviceName(const PaDeviceInfo* info)
{
#if USE_PORTAUDIO_V19
wxString hostapiName(Pa_GetHostApiInfo(info->hostApi)->name, wxConvLocal);
wxString infoName(info->name, wxConvLocal);
return wxString::Format(wxT("%s: %s"),
hostapiName.c_str(),
infoName.c_str());
#else
return wxString(info->name, wxConvLocal);
#endif
}
AudioIO::AudioIO()
{
mAudioThreadShouldCallFillBuffersOnce = false;
mAudioThreadFillBuffersLoopRunning = false;
mAudioThreadFillBuffersLoopActive = false;
#if USE_PORTAUDIO_V19
mPortStreamV19 = NULL;
#else
mPortStreamV18 = NULL;
mInCallbackFinishedState = false;
#endif
mStreamToken = 0;
mStopStreamCount = 0;
mTempFloats = new float[65536];
mLastPaError = paNoError;
mLastRecordingOffset = 0.0;
mNumCaptureChannels = 0;
mPaused = false;
mPlayLooped = false;
mListener = NULL;
mUpdateMeters = false;
mUpdatingMeters = false;
PaError err = Pa_Initialize();
if (err != paNoError) {
wxString errStr = _("Could not find any audio devices.\n");
errStr += _("You will not be able to play or record audio.\n\n");
wxString paErrStr = LAT1CTOWX(Pa_GetErrorText(err));
if (paErrStr)
errStr += _("Error: ")+paErrStr;
wxMessageBox(errStr, _("Error Initializing Audio"), wxICON_ERROR|wxOK);
}
mThread = new AudioThread();
mThread->Create();
#if defined(USE_PORTMIXER)
mPortMixer = NULL;
mPreviousHWPlaythrough = -1.0;
HandleDeviceChange();
#else
mEmulateMixerOutputVol = true;
mMixerOutputVol = 1.0;
mEmulateMixerInputVol = true;
mMixerInputVol = 1.0;
#endif
}
AudioIO::~AudioIO()
{
#if defined(USE_PORTMIXER)
if( mPortMixer ) {
#if __WXMAC__
if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
#endif
Px_CloseMixer(mPortMixer);
}
mPortMixer = NULL;
#endif
Pa_Terminate();
wxYield();
mThread->Delete();
delete [] mTempFloats;
delete mThread;
}
void AudioIO::SetMixer(int recordDevice, float recordVolume,
float playbackVolume)
{
mMixerOutputVol = playbackVolume;
mMixerInputVol = recordVolume;
#if defined(USE_PORTMIXER)
PxMixer *mixer = mPortMixer;
if( mixer )
{
int oldRecordDevice = Px_GetCurrentInputSource(mixer);
float oldRecordVolume = Px_GetInputVolume(mixer);
float oldPlaybackVolume = Px_GetPCMOutputVolume(mixer);
if( recordDevice != oldRecordDevice )
Px_SetCurrentInputSource(mixer, recordDevice);
if( oldRecordVolume != recordVolume )
Px_SetInputVolume(mixer, recordVolume);
if( oldPlaybackVolume != playbackVolume )
Px_SetPCMOutputVolume(mixer, playbackVolume);
return;
}
#endif
}
void AudioIO::GetMixer(int *recordDevice, float *recordVolume,
float *playbackVolume)
{
#if defined(USE_PORTMIXER)
PxMixer *mixer = mPortMixer;
if( mixer )
{
*recordDevice = Px_GetCurrentInputSource(mixer);
if (mEmulateMixerInputVol)
*recordVolume = mMixerInputVol;
else
*recordVolume = Px_GetInputVolume(mixer);
if (mEmulateMixerOutputVol)
*playbackVolume = mMixerOutputVol;
else
*playbackVolume = Px_GetPCMOutputVolume(mixer);
return;
}
#endif
*recordDevice = 0;
*recordVolume = mMixerInputVol;
*playbackVolume = mMixerOutputVol;
}
wxArrayString AudioIO::GetInputSourceNames()
{
#if defined(USE_PORTMIXER)
wxArrayString deviceNames;
if( mPortMixer )
{
int numSources = Px_GetNumInputSources(mPortMixer);
for( int source = 0; source < numSources; source++ )
deviceNames.Add(LAT1CTOWX(Px_GetInputSourceName(mPortMixer, source)));
}
return deviceNames;
#else
wxArrayString blank;
return blank;
#endif
}
void AudioIO::HandleDeviceChange()
{
if( IsStreamActive() )
return;
#if defined(USE_PORTMIXER)
const PaDeviceInfo* info;
if( mPortMixer )
Px_CloseMixer(mPortMixer);
mPortMixer = NULL;
#if USE_PORTAUDIO_V19
int recDeviceNum = Pa_GetDefaultInputDevice();
int playDeviceNum = Pa_GetDefaultOutputDevice();
#else
int recDeviceNum = Pa_GetDefaultInputDeviceID();
int playDeviceNum = Pa_GetDefaultOutputDeviceID();
#endif
if (recDeviceNum < 0)
recDeviceNum = 0;
if (playDeviceNum < 0)
playDeviceNum = 0;
wxString recDevice = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
wxString playDevice = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
int j;
#if USE_PORTAUDIO_V19
if (Pa_GetDeviceCount() <= 0)
return;
for(j=0; j<Pa_GetDeviceCount(); j++) {
#else
if (Pa_CountDevices() <= 0)
return;
for(j=0; j<Pa_CountDevices(); j++) {
#endif
info = Pa_GetDeviceInfo(j);
if(!info)
continue;
if (DeviceName(info) == playDevice && info->maxOutputChannels > 0)
playDeviceNum = j;
if (DeviceName(info) == recDevice && info->maxInputChannels > 0)
recDeviceNum = j;
}
wxArrayLong supportedSampleRates = GetSupportedSampleRates(playDevice, recDevice);
int highestSampleRate = supportedSampleRates[supportedSampleRates.GetCount() - 1];
mEmulateMixerInputVol = true;
mEmulateMixerOutputVol = true;
mMixerInputVol = 1.0;
mMixerOutputVol = 1.0;
int error;
#if USE_PORTAUDIO_V19
PaStream *stream;
PaStreamParameters playbackParameters;
playbackParameters.device = playDeviceNum;
playbackParameters.sampleFormat = paFloat32;
playbackParameters.hostApiSpecificStreamInfo = NULL;
playbackParameters.channelCount = 2;
if (Pa_GetDeviceInfo(playDeviceNum))
playbackParameters.suggestedLatency =
Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
else
playbackParameters.suggestedLatency = 100;
PaStreamParameters captureParameters;
captureParameters.device = recDeviceNum;
captureParameters.sampleFormat = paFloat32;;
captureParameters.hostApiSpecificStreamInfo = NULL;
captureParameters.channelCount = 2;
if (Pa_GetDeviceInfo(recDeviceNum))
captureParameters.suggestedLatency =
Pa_GetDeviceInfo(recDeviceNum)->defaultLowOutputLatency;
else
captureParameters.suggestedLatency = 100;
error = Pa_OpenStream(&stream,
&captureParameters, &playbackParameters,
highestSampleRate, paFramesPerBufferUnspecified,
paClipOff | paDitherOff,
audacityAudioCallback, NULL);
if( error ) {
error = Pa_OpenStream(&stream,
&captureParameters, NULL,
highestSampleRate, paFramesPerBufferUnspecified,
paClipOff | paDitherOff,
audacityAudioCallback, NULL);
}
#else
PortAudioStream *stream;
error = Pa_OpenStream(&stream, recDeviceNum, 2, paFloat32, NULL,
playDeviceNum, 2, paFloat32, NULL,
highestSampleRate, 512, 1, paClipOff | paDitherOff,
audacityAudioCallback, NULL);
if( error ) {
error = Pa_OpenStream(&stream, recDeviceNum, 2, paFloat32, NULL,
paNoDevice, 0, paFloat32, NULL,
highestSampleRate, 512, 1, paClipOff | paDitherOff,
audacityAudioCallback, NULL);
}
#endif
if( error )
return;
mPortMixer = Px_OpenMixer(stream, 0);
if (!mPortMixer) {
Pa_CloseStream(stream);
return;
}
mMixerOutputVol = Px_GetPCMOutputVolume(mPortMixer);
mEmulateMixerOutputVol = false;
Px_SetPCMOutputVolume(mPortMixer, 0.0);
if (Px_GetPCMOutputVolume(mPortMixer) > 0.1)
mEmulateMixerOutputVol = true;
Px_SetPCMOutputVolume(mPortMixer, 0.2f);
if (Px_GetPCMOutputVolume(mPortMixer) < 0.1 ||
Px_GetPCMOutputVolume(mPortMixer) > 0.3)
mEmulateMixerOutputVol = true;
Px_SetPCMOutputVolume(mPortMixer, mMixerOutputVol);
mMixerInputVol = Px_GetInputVolume(mPortMixer);
mEmulateMixerInputVol = false;
Px_SetInputVolume(mPortMixer, 0.0);
if (Px_GetInputVolume(mPortMixer) > 0.1)
mEmulateMixerInputVol = true;
Px_SetInputVolume(mPortMixer, 0.2f);
if (Px_GetInputVolume(mPortMixer) < 0.1 ||
Px_GetInputVolume(mPortMixer) > 0.3)
mEmulateMixerInputVol = true;
Px_SetInputVolume(mPortMixer, mMixerInputVol);
Pa_CloseStream(stream);
#if 0
printf("PortMixer: Output: %s Input: %s\n",
mEmulateMixerOutputVol? "emulated": "native",
mEmulateMixerInputVol? "emulated": "native");
#endif
mMixerInputVol = 1.0;
mMixerOutputVol = 1.0;
#endif
}
PaSampleFormat AudacityToPortAudioSampleFormat(sampleFormat format)
{
switch(format) {
case int16Sample:
return paInt16;
case int24Sample:
return paInt24;
case floatSample:
default:
return paFloat32;
}
}
bool AudioIO::StartPortAudioStream(double sampleRate,
unsigned int numPlaybackChannels,
unsigned int numCaptureChannels,
sampleFormat captureFormat)
{
mLastPaError = paNoError;
mRate = GetBestRate(numCaptureChannels > 0, sampleRate);
if (mListener) {
mListener->OnAudioIORate((int)mRate);
}
if (captureFormat == int24Sample)
captureFormat = floatSample;
mNumPlaybackChannels = numPlaybackChannels;
mNumCaptureChannels = numCaptureChannels;
#if USE_PORTAUDIO_V19
PaStreamParameters *playbackParameters = NULL;
PaStreamParameters *captureParameters = NULL;
double latencyDuration = 100.0;
gPrefs->Read(wxT("/AudioIO/LatencyDuration"), &latencyDuration);
if( numPlaybackChannels > 0)
{
playbackParameters = new PaStreamParameters;
wxString playbackDeviceName = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
const PaDeviceInfo *playbackDeviceInfo;
playbackParameters->device = Pa_GetDefaultOutputDevice();
if (playbackParameters->device < 0)
playbackParameters->device = 0;
if( playbackDeviceName != wxT("") )
{
for( int i = 0; i < Pa_GetDeviceCount(); i++)
{
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
if(!info)
continue;
if (DeviceName(info) == playbackDeviceName && info->maxOutputChannels > 0)
playbackParameters->device = i;
}
}
playbackDeviceInfo = Pa_GetDeviceInfo( playbackParameters->device );
if( playbackDeviceInfo == NULL )
return false;
playbackParameters->sampleFormat = paFloat32;
playbackParameters->hostApiSpecificStreamInfo = NULL;
playbackParameters->channelCount = mNumPlaybackChannels;
if (mSoftwarePlaythrough)
playbackParameters->suggestedLatency =
playbackDeviceInfo->defaultLowOutputLatency;
else
playbackParameters->suggestedLatency = latencyDuration/1000.0;
}
if( numCaptureChannels > 0)
{
mCaptureFormat = captureFormat;
captureParameters = new PaStreamParameters;
const PaDeviceInfo *captureDeviceInfo;
wxString captureDeviceName = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
captureParameters->device = Pa_GetDefaultInputDevice();
if (captureParameters->device < 0)
captureParameters->device = 0;
if( captureDeviceName != wxT("") )
{
for( int i = 0; i < Pa_GetDeviceCount(); i++)
{
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
if(!info)
continue;
if (DeviceName(info) == captureDeviceName && info->maxInputChannels > 0)
captureParameters->device = i;
}
}
captureDeviceInfo = Pa_GetDeviceInfo( captureParameters->device );
if( captureDeviceInfo == NULL )
return false;
captureParameters->sampleFormat =
AudacityToPortAudioSampleFormat(mCaptureFormat);
captureParameters->hostApiSpecificStreamInfo = NULL;
captureParameters->channelCount = mNumCaptureChannels;
if (mSoftwarePlaythrough)
captureParameters->suggestedLatency =
captureDeviceInfo->defaultHighInputLatency;
else
captureParameters->suggestedLatency = latencyDuration/1000.0;
}
mLastPaError = Pa_OpenStream( &mPortStreamV19,
captureParameters, playbackParameters,
mRate, paFramesPerBufferUnspecified,
paNoFlag,
audacityAudioCallback, NULL );
#if USE_PORTMIXER
if (mPortMixer) {
#if __WXMAC__
if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
#endif
Px_CloseMixer(mPortMixer);
}
mPortMixer = NULL;
if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
mPortMixer = Px_OpenMixer(mPortStreamV19, 0);
#ifdef __MACOSX__
if (mPortMixer) {
if (Px_SupportsPlaythrough(mPortMixer)) {
bool playthrough;
mPreviousHWPlaythrough = Px_GetPlaythrough(mPortMixer);
gPrefs->Read(wxT("/AudioIO/Playthrough"), &playthrough, false);
if (playthrough)
Px_SetPlaythrough(mPortMixer, 1.0);
else
Px_SetPlaythrough(mPortMixer, 0.0);
}
}
#endif
}
#endif
delete captureParameters;
delete playbackParameters;
#else
PaDeviceID captureDevice = paNoDevice,
playbackDevice = paNoDevice;
PaSampleFormat paCaptureFormat = paFloat32;
if( numPlaybackChannels > 0 )
{
playbackDevice = Pa_GetDefaultOutputDeviceID();
wxString playbackDeviceName = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
if( playbackDeviceName != wxT("") )
{
for( int i = 0; i < Pa_CountDevices(); i++)
{
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
if(!info)
continue;
if (DeviceName(info) == playbackDeviceName && info->maxOutputChannels > 0)
playbackDevice = i;
}
}
}
if( numCaptureChannels > 0 )
{
mCaptureFormat = captureFormat;
captureDevice = Pa_GetDefaultInputDeviceID();
wxString captureDeviceName = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
if( captureDeviceName != wxT("") )
{
for( int i = 0; i < Pa_CountDevices(); i++)
{
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
if(!info)
continue;
if (DeviceName(info) == captureDeviceName && info->maxInputChannels > 0)
captureDevice = i;
}
}
paCaptureFormat =
AudacityToPortAudioSampleFormat(mCaptureFormat);
}
mPortStreamV18 = NULL;
mLastPaError = Pa_OpenStream( &mPortStreamV18,
captureDevice,
mNumCaptureChannels,
paCaptureFormat,
NULL,
playbackDevice,
mNumPlaybackChannels,
paFloat32,
NULL,
mRate, 256, 0,
paClipOff | paDitherOff,
audacityAudioCallback, NULL );
#if USE_PORTMIXER
if (mPortMixer) {
#if __WXMAC__
if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
#endif
Px_CloseMixer(mPortMixer);
}
mPortMixer = NULL;
if (mPortStreamV18 != NULL && mLastPaError == paNoError) {
mPortMixer = Px_OpenMixer(mPortStreamV18, 0);
#ifdef __MACOSX__
if (mPortMixer) {
if (Px_SupportsPlaythrough(mPortMixer)) {
bool playthrough;
mPreviousHWPlaythrough = Px_GetPlaythrough(mPortMixer);
gPrefs->Read(wxT("/AudioIO/Playthrough"), &playthrough, false);
if (playthrough)
Px_SetPlaythrough(mPortMixer, 1.0);
else
Px_SetPlaythrough(mPortMixer, 0.0);
}
}
#endif
}
#endif
mInCallbackFinishedState = false;
#endif
return (mLastPaError == paNoError);
}
void AudioIO::StartMonitoring(double sampleRate)
{
#if USE_PORTAUDIO_V19
if ( mPortStreamV19 || mStreamToken )
return;
#else
if ( mPortStreamV18 || mStreamToken )
return;
#endif
bool success;
long captureChannels;
sampleFormat captureFormat = (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
gPrefs->Read(wxT("/AudioIO/RecordChannels"), &captureChannels, 1L);
gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
int playbackChannels = 0;
if (mSoftwarePlaythrough)
playbackChannels = 2;
success = StartPortAudioStream(sampleRate, (unsigned int)playbackChannels,
(unsigned int)captureChannels,
captureFormat);
#if USE_PORTAUDIO_V19
mLastPaError = Pa_StartStream( mPortStreamV19 );
#else
mLastPaError = Pa_StartStream( mPortStreamV18 );
#endif
}
int AudioIO::StartStream(WaveTrackArray playbackTracks,
WaveTrackArray captureTracks,
TimeTrack *timeTrack, double sampleRate,
double t0, double t1,
AudioIOListener* listener,
bool playLooped ,
double cutPreviewGapStart ,
double cutPreviewGapLen )
{
if( IsBusy() )
return 0;
mStreamToken--;
if (mStreamToken != -1)
return 0;
#if USE_PORTAUDIO_V19
if (mPortStreamV19) {
StopStream();
while(mPortStreamV19)
wxMilliSleep( 50 );
}
#else
if (mPortStreamV18) {
StopStream();
while(mPortStreamV18)
wxMilliSleep( 50 );
}
#endif
gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
mListener = listener;
mInputMeter = NULL;
mOutputMeter = NULL;
mRate = sampleRate;
mT0 = t0;
mT = t0;
mT1 = t1;
mTime = t0;
mSeek = 0;
mLastRecordingOffset = 0;
mPlaybackTracks = playbackTracks;
mCaptureTracks = captureTracks;
mPlayLooped = playLooped;
mCutPreviewGapStart = cutPreviewGapStart;
mCutPreviewGapLen = cutPreviewGapLen;
mPlaySpeed = 1.0;
if (timeTrack) {
mPlaySpeed = timeTrack->GetEnvelope()->GetValue(t0);
mPlaySpeed = 1 / ((timeTrack->GetRangeLower() * (1 - mPlaySpeed) +
mPlaySpeed * timeTrack->GetRangeUpper())/100.0);
}
mWarpedT1 = mT0 + ((mT1 - mT0) * mPlaySpeed);
mPlaybackRingBufferSecs = 4.5 + (0.5 * mPlaybackTracks.GetCount());
mMaxPlaybackSecsToCopy = 0.75 + (0.25 * mPlaybackTracks.GetCount());
mCaptureRingBufferSecs = 4.5 + 0.5 * mCaptureTracks.GetCount();
mMinCaptureSecsToCopy = 0.2 + (0.2 * mCaptureTracks.GetCount());
unsigned int playbackChannels = 0;
unsigned int captureChannels = 0;
sampleFormat captureFormat = floatSample;
if( playbackTracks.GetCount() > 0 )
playbackChannels = 2;
if (mSoftwarePlaythrough)
playbackChannels = 2;
if( captureTracks.GetCount() > 0 )
{
captureChannels = mCaptureTracks.GetCount();
captureFormat = mCaptureTracks[0]->GetSampleFormat();
if (mListener)
mListener->OnAudioIOStartRecording();
}
bool success = StartPortAudioStream(sampleRate, playbackChannels,
captureChannels, captureFormat);
if (!success) {
if (mListener && captureChannels > 0)
mListener->OnAudioIOStopRecording();
mStreamToken = 0;
return 0;
}
if( mNumPlaybackChannels > 0 )
{
sampleCount playbackBufferSize =
(sampleCount)(mRate * mPlaybackRingBufferSecs + 0.5);
sampleCount playbackMixBufferSize =
(sampleCount)(mRate * mMaxPlaybackSecsToCopy + 0.5);
mPlaybackBuffers = new RingBuffer* [mPlaybackTracks.GetCount()];
mPlaybackMixers = new Mixer* [mPlaybackTracks.GetCount()];
for( unsigned int i = 0; i < mPlaybackTracks.GetCount(); i++ )
{
mPlaybackBuffers[i] = new RingBuffer(floatSample, playbackBufferSize);
mPlaybackMixers[i] = new Mixer(1, &mPlaybackTracks[i],
timeTrack, mT0, mWarpedT1, 1,
playbackMixBufferSize, false,
mRate, floatSample, false);
mPlaybackMixers[i]->ApplyTrackGains(false);
}
}
if( mNumCaptureChannels > 0 )
{
sampleCount captureBufferSize =
(sampleCount)(mRate * mCaptureRingBufferSecs + 0.5);
mCaptureBuffers = new RingBuffer* [mCaptureTracks.GetCount()];
mResample = new Resample* [mCaptureTracks.GetCount()];
mFactor = sampleRate / mRate;
for( unsigned int i = 0; i < mCaptureTracks.GetCount(); i++ )
{
mCaptureBuffers[i] = new RingBuffer( mCaptureTracks[i]->GetSampleFormat(),
captureBufferSize );
mResample[i] = new Resample( true, mFactor, mFactor );
}
}
mAudioThreadShouldCallFillBuffersOnce = true;
while( mAudioThreadShouldCallFillBuffersOnce == true )
wxMilliSleep( 50 );
PaError err;
#if USE_PORTAUDIO_V19
err = Pa_StartStream( mPortStreamV19 );
#else
err = Pa_StartStream( mPortStreamV18 );
#endif
if( err != paNoError )
{
if (mListener && mNumCaptureChannels > 0)
mListener->OnAudioIOStopRecording();
wxPrintf(wxT("%hs\n"), Pa_GetErrorText(err));
mStreamToken = 0;
return 0;
}
mAudioThreadFillBuffersLoopRunning = true;
mStreamToken = (++mNextStreamToken);
return mStreamToken;
}
void AudioIO::SetMeters(Meter *inputMeter, Meter *outputMeter)
{
mInputMeter = inputMeter;
mOutputMeter = outputMeter;
if (mInputMeter)
mInputMeter->Reset(mRate, true);
if (mOutputMeter)
mOutputMeter->Reset(mRate, true);
mUpdateMeters = true;
}
void AudioIO::StopStream()
{
#if USE_PORTAUDIO_V19
if( mPortStreamV19 == NULL )
return;
if( Pa_IsStreamStopped( mPortStreamV19 ) )
return;
#else
if( mPortStreamV18 == NULL )
return;
if( IsStreamActive() == false && mInCallbackFinishedState == false )
return;
#endif
mStopStreamCount++;
if (mStopStreamCount != 1)
return;
mAudioThreadFillBuffersLoopRunning = false;
mUpdateMeters = false;
while(mUpdatingMeters) {
wxYield();
wxMilliSleep( 50 );
}
#if defined(USE_PORTMIXER)
if( mPortMixer ) {
#if __WXMAC__
if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
#endif
}
#endif
#if USE_PORTAUDIO_V19
if (mPortStreamV19) {
Pa_AbortStream( mPortStreamV19 );
Pa_CloseStream( mPortStreamV19 );
mPortStreamV19 = NULL;
}
#else
if (mPortStreamV18) {
if (mInCallbackFinishedState)
Pa_StopStream( mPortStreamV18 );
else
Pa_AbortStream( mPortStreamV18 );
Pa_CloseStream( mPortStreamV18 );
mPortStreamV18 = NULL;
mInCallbackFinishedState = false;
}
#endif
if (mStreamToken > 0) {
mAudioThreadShouldCallFillBuffersOnce = true;
while( mAudioThreadShouldCallFillBuffersOnce == true )
{
wxGetApp().Yield( true );
wxMilliSleep( 50 );
}
if( mPlaybackTracks.GetCount() > 0 )
{
for( unsigned int i = 0; i < mPlaybackTracks.GetCount(); i++ )
{
delete mPlaybackBuffers[i];
delete mPlaybackMixers[i];
}
delete[] mPlaybackBuffers;
delete[] mPlaybackMixers;
}
double latencyCorrection = 0;
gPrefs->Read(wxT("/AudioIO/LatencyCorrection"), &latencyCorrection);
if( mCaptureTracks.GetCount() > 0 )
{
for( unsigned int i = 0; i < mCaptureTracks.GetCount(); i++ )
{
delete mCaptureBuffers[i];
mCaptureTracks[i]->Flush();
mCaptureTracks[i]->Offset(mLastRecordingOffset +
latencyCorrection/1000.0);
}
delete[] mCaptureBuffers;
}
}
if (mInputMeter)
mInputMeter->Reset(mRate, false);
if (mOutputMeter)
mOutputMeter->Reset(mRate, false);
if (mListener && mNumCaptureChannels > 0)
mListener->OnAudioIOStopRecording();
mStreamToken = 0;
mStopStreamCount = 0;
}
void AudioIO::SetPaused(bool state)
{
mPaused = state;
}
bool AudioIO::IsPaused()
{
return mPaused;
}
bool AudioIO::IsBusy()
{
#if 0
if (IsStreamActive())
return true;
#if USE_PORTAUDIO_V19
if (mPortStreamV19)
return true;
#else
if (mPortStreamV18)
return true;
#endif
#endif
if (mStreamToken != 0)
return true;
return false;
}
bool AudioIO::IsStreamActive()
{
#if USE_PORTAUDIO_V19
if( mPortStreamV19 )
return Pa_IsStreamActive( mPortStreamV19 ) != 0;
else
return false;
#else
if( mPortStreamV18 &&
Pa_StreamActive( mPortStreamV18 ) &&
mInCallbackFinishedState == false )
return true;
else
return false;
#endif
}
bool AudioIO::IsStreamActive(int token)
{
if( IsStreamActive() && token > 0 && token == mStreamToken )
return true;
else
return false;
}
bool AudioIO::IsAudioTokenActive(int token)
{
return ( token > 0 && token == mStreamToken );
}
bool AudioIO::IsMonitoring()
{
#if USE_PORTAUDIO_V19
return ( mPortStreamV19 && mStreamToken==0 );
#else
return ( mPortStreamV18 && mStreamToken==0 );
#endif
}
double AudioIO::NormalizeStreamTime(double absoluteTime) const
{
if (absoluteTime < mT0)
absoluteTime = mT0;
if (mPlayLooped) {
while (absoluteTime > mT1)
absoluteTime -= mT1 - mT0;
}
else {
if (absoluteTime > mT1)
absoluteTime = mT1;
}
if (mCutPreviewGapLen > 0)
{
if (absoluteTime > mCutPreviewGapStart)
absoluteTime += mCutPreviewGapLen;
}
return absoluteTime;
}
double AudioIO::GetStreamTime()
{
if( !IsStreamActive() )
return -1000000000;
return NormalizeStreamTime(mTime);
}
wxArrayLong AudioIO::GetSupportedPlaybackRates(wxString devName, double rate)
{
wxArrayLong supported;
int irate = (int)rate;
const PaDeviceInfo* devInfo = NULL;
int devIndex = -1;
int i;
if (devName.IsEmpty())
{
devName = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
}
#if USE_PORTAUDIO_V19
for (i = 0; i < Pa_GetDeviceCount(); i++)
#else
for (i = 0; i < Pa_CountDevices(); i++)
#endif
{
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
if (info && DeviceName(info) == devName && info->maxOutputChannels > 0)
{
devInfo = info;
devIndex = i;
break;
}
}
if (!devInfo)
{
return supported;
}
#if USE_PORTAUDIO_V19
PaStreamParameters pars;
pars.device = devIndex;
pars.channelCount = 2;
pars.sampleFormat = paFloat32;
pars.suggestedLatency = devInfo->defaultHighOutputLatency;
pars.hostApiSpecificStreamInfo = NULL;
for (i = 0; i < NumStandardRates; i++)
{
if (Pa_IsFormatSupported(NULL, &pars, StandardRates[i]) == 0)
{
supported.Add(StandardRates[i]);
}
}
if (irate != 0 && supported.Index(irate) == wxNOT_FOUND)
{
if (Pa_IsFormatSupported(NULL, &pars, irate) == 0)
{
supported.Add(irate);
}
}
#else
if (devInfo->numSampleRates == -1)
{
for (i = 0; i < NumStandardRates; i++)
{
if (StandardRates[i] >= devInfo->sampleRates[0] &&
StandardRates[i] <= devInfo->sampleRates[1])
{
supported.Add(StandardRates[i]);
}
}
if (irate != 0 && supported.Index(irate) == wxNOT_FOUND)
{
if (irate >= devInfo->sampleRates[0] &&
irate <= devInfo->sampleRates[1])
{
supported.Add(irate);
}
}
}
else
{
for (i = 0; i < devInfo->numSampleRates; i++)
{
supported.Add((int)devInfo->sampleRates[i]);
}
}
#endif
return supported;
}
wxArrayLong AudioIO::GetSupportedCaptureRates(wxString devName, double rate)
{
wxArrayLong supported;
int irate = (int)rate;
const PaDeviceInfo* devInfo = NULL;
int devIndex = -1;
int i;
if (devName.IsEmpty())
{
devName = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
}
#if USE_PORTAUDIO_V19
double latencyDuration = 100.0;
long recordChannels = 1;
gPrefs->Read(wxT("/AudioIO/LatencyDuration"), &latencyDuration);
gPrefs->Read(wxT("/AudioIO/RecordChannels"), &recordChannels);
#endif
#if USE_PORTAUDIO_V19
for (i = 0; i < Pa_GetDeviceCount(); i++)
#else
for (i = 0; i < Pa_CountDevices(); i++)
#endif
{
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
if (info && DeviceName(info) == devName && info->maxInputChannels > 0)
{
devInfo = info;
devIndex = i;
break;
}
}
if (!devInfo)
{
return supported;
}
#if USE_PORTAUDIO_V19
PaStreamParameters pars;
pars.device = devIndex;
pars.channelCount = recordChannels;
pars.sampleFormat = paFloat32;
pars.suggestedLatency = latencyDuration / 1000.0;
pars.hostApiSpecificStreamInfo = NULL;
for (i = 0; i < NumStandardRates; i++)
{
if (Pa_IsFormatSupported(NULL, &pars, StandardRates[i]) == 0)
{
supported.Add(StandardRates[i]);
}
}
if (irate != 0 && supported.Index(irate) == wxNOT_FOUND)
{
if (Pa_IsFormatSupported(NULL, &pars, irate) == 0)
{
supported.Add(irate);
}
}
#else
if (devInfo->numSampleRates == -1)
{
for (i = 0; i < NumStandardRates; i++)
{
if (StandardRates[i] >= devInfo->sampleRates[0] &&
StandardRates[i] <= devInfo->sampleRates[1])
{
supported.Add(StandardRates[i]);
}
}
if (irate != 0 && supported.Index(irate) == wxNOT_FOUND)
{
if (irate >= devInfo->sampleRates[0] &&
irate <= devInfo->sampleRates[1])
{
supported.Add(irate);
}
}
}
else
{
for (i = 0; i < devInfo->numSampleRates; i++)
{
supported.Add((int)devInfo->sampleRates[i]);
}
}
#endif
return supported;
}
wxArrayLong AudioIO::GetSupportedSampleRates(wxString playDevice, wxString recDevice, double rate)
{
wxArrayLong playback = GetSupportedPlaybackRates(playDevice, rate);
wxArrayLong capture = GetSupportedCaptureRates(recDevice, rate);
int i;
wxArrayLong result;
for (i = 0; i < (int)playback.GetCount(); i++)
if (capture.Index(playback[i]) != wxNOT_FOUND)
result.Add(playback[i]);
if (result.IsEmpty())
{
for (i = 0; i < NumStandardRates; i++)
result.Add(StandardRates[i]);
}
return result;
}
int AudioIO::GetOptimalSupportedSampleRate()
{
wxArrayLong rates = GetSupportedSampleRates();
if (rates.Index(44100) != wxNOT_FOUND)
return 44100;
if (rates.Index(48000) != wxNOT_FOUND)
return 48000;
return rates[rates.GetCount() - 1];
}
long AudioIO::GetBestRate(bool capturing, double sampleRate)
{
wxArrayLong rates;
if (capturing) {
rates = GetSupportedCaptureRates(wxT(""), sampleRate);
}
else {
rates = GetSupportedPlaybackRates(wxT(""), sampleRate);
}
long rate = (long)sampleRate;
if (rates.Index(rate) != wxNOT_FOUND) {
return rate;
}
if (rates.IsEmpty() || rate < 44100 && rates.Index(44100)) {
return 44100;
}
if (rate < 48000 && rates.Index(48000)) {
return 48000;
}
if (rate < 96000 && rates.Index(96000)) {
return 96000;
}
return rates[rates.GetCount() - 1];
}
AudioThread::ExitCode AudioThread::Entry()
{
while( !TestDestroy() )
{
if( gAudioIO->mAudioThreadShouldCallFillBuffersOnce )
{
gAudioIO->FillBuffers();
gAudioIO->mAudioThreadShouldCallFillBuffersOnce = false;
}
else if( gAudioIO->mAudioThreadFillBuffersLoopRunning )
{
gAudioIO->FillBuffers();
}
Sleep(10);
}
return 0;
}
int AudioIO::GetCommonlyAvailPlayback()
{
int commonlyAvail = mPlaybackBuffers[0]->AvailForPut();
unsigned int i;
for( i = 1; i < mPlaybackTracks.GetCount(); i++ )
{
int thisBlockAvail = mPlaybackBuffers[i]->AvailForPut();
if( thisBlockAvail < commonlyAvail )
commonlyAvail = thisBlockAvail;
}
return commonlyAvail;
}
int AudioIO::GetCommonlyAvailCapture()
{
int commonlyAvail = mCaptureBuffers[0]->AvailForGet();
unsigned int i;
for( i = 1; i < mCaptureTracks.GetCount(); i++ )
{
int avail = mCaptureBuffers[i]->AvailForGet();
if( avail < commonlyAvail )
commonlyAvail = avail;
}
return commonlyAvail;
}
void AudioIO::FillBuffers()
{
unsigned int i;
gAudioIO->mAudioThreadFillBuffersLoopActive = true;
if( mPlaybackTracks.GetCount() > 0 )
{
int commonlyAvail = GetCommonlyAvailPlayback();
double secsAvail = commonlyAvail / mRate;
if (secsAvail >= mMaxPlaybackSecsToCopy ||
(!mPlayLooped && (secsAvail > 0 && mT+secsAvail >= mWarpedT1)))
{
if (secsAvail > mMaxPlaybackSecsToCopy)
secsAvail = mMaxPlaybackSecsToCopy;
double deltat;
do {
deltat = secsAvail;
if( mT + deltat > mWarpedT1 )
{
deltat = mWarpedT1 - mT;
if( deltat < 0.0 )
deltat = 0.0;
}
mT += deltat;
secsAvail -= deltat;
for( i = 0; i < mPlaybackTracks.GetCount(); i++ )
{
int processed =
mPlaybackMixers[i]->Process(lrint(deltat * mRate));
samplePtr warpedSamples = mPlaybackMixers[i]->GetBuffer();
mPlaybackBuffers[i]->Put(warpedSamples, floatSample, processed);
}
if (mPlayLooped && mT >= mWarpedT1)
{
for (i = 0; i < mPlaybackTracks.GetCount(); i++)
mPlaybackMixers[i]->Restart();
mT = mT0;
}
} while (mPlayLooped && secsAvail > 0 && deltat > 0);
}
}
if( mCaptureTracks.GetCount() > 0 )
{
int commonlyAvail = GetCommonlyAvailCapture();
double deltat = commonlyAvail / mRate;
if (mAudioThreadShouldCallFillBuffersOnce ||
deltat >= mMinCaptureSecsToCopy)
{
XMLStringWriter blockFileLog;
int numChannels = mCaptureTracks.GetCount();
for( i = 0; (int)i < numChannels; i++ )
{
int avail = commonlyAvail;
sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
XMLStringWriter appendLog;
if( mFactor == 1.0 )
{
samplePtr temp = NewSamples(avail, trackFormat);
mCaptureBuffers[i]->Get (temp, trackFormat, avail);
mCaptureTracks[i]-> Append(temp, trackFormat, avail, 1,
&appendLog);
DeleteSamples(temp);
}
else
{
int size = lrint(avail * mFactor);
samplePtr temp1 = NewSamples(avail, floatSample);
samplePtr temp2 = NewSamples(size, floatSample);
mCaptureBuffers[i]->Get(temp1, floatSample, avail);
size = mResample[i]->Process(mFactor, (float *)temp1, avail, true,
&size, (float *)temp2, size);
mCaptureTracks[i]-> Append(temp2, floatSample, size, 1,
&appendLog);
DeleteSamples(temp1);
DeleteSamples(temp2);
}
if (!appendLog.IsEmpty())
{
blockFileLog.StartTag(wxT("recordingrecovery"));
blockFileLog.WriteAttr(wxT("channel"), (int)i);
blockFileLog.WriteAttr(wxT("numchannels"), numChannels);
blockFileLog.WriteSubTree(appendLog);
blockFileLog.EndTag(wxT("recordingrecovery"));
}
}
if (mListener && !blockFileLog.IsEmpty())
mListener->OnAudioIONewBlockFiles(blockFileLog);
}
}
gAudioIO->mAudioThreadFillBuffersLoopActive = false;
}
#define MAX(a,b) ((a) > (b) ? (a) : (b))
void DoSoftwarePlaythrough(const void *inputBuffer,
sampleFormat inputFormat,
int inputChannels,
float *outputBuffer,
int len,
float gain)
{
float *tempBuffer = (float *)alloca(len * sizeof(float));
int i, j;
for(j=0; j<inputChannels; j++) {
samplePtr inputPtr = ((samplePtr)inputBuffer) + (j * SAMPLE_SIZE(inputFormat));
CopySamples(inputPtr, inputFormat,
(samplePtr)tempBuffer, floatSample,
len, true, inputChannels);
for(i=0; i<len; i++)
outputBuffer[2*i + (j%2)] = tempBuffer[i];
if (inputChannels == 1)
for(i=0; i<len; i++)
outputBuffer[2*i + 1] = tempBuffer[i];
}
}
#if USE_PORTAUDIO_V19
int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData )
#else
#define paContinue 0
int audacityAudioCallback(void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
#endif
{
int numPlaybackChannels = gAudioIO->mNumPlaybackChannels;
int numPlaybackTracks = gAudioIO->mPlaybackTracks.GetCount();
int numCaptureChannels = gAudioIO->mNumCaptureChannels;
int callbackReturn = paContinue;
void *tempBuffer = alloca(framesPerBuffer*sizeof(float)*
MAX(numCaptureChannels,numPlaybackChannels));
float *tempFloats = (float*)tempBuffer;
unsigned int i;
int t;
if( gAudioIO->mPaused )
{
if (outputBuffer && numPlaybackChannels > 0)
{
ClearSamples((samplePtr)outputBuffer, floatSample,
0, framesPerBuffer * numPlaybackChannels);
if (inputBuffer && gAudioIO->mSoftwarePlaythrough) {
float gain = 1.0;
if (gAudioIO->mEmulateMixerInputVol)
gain = gAudioIO->mMixerInputVol;
DoSoftwarePlaythrough(inputBuffer, gAudioIO->mCaptureFormat,
numCaptureChannels,
(float *)outputBuffer, (int)framesPerBuffer, gain);
}
}
return paContinue;
}
if (gAudioIO->mStreamToken > 0)
{
if( outputBuffer && (numPlaybackChannels > 0) )
{
bool cut = false;
bool linkFlag = false;
float *outputFloats = (float *)outputBuffer;
for( i = 0; i < framesPerBuffer*numPlaybackChannels; i++)
outputFloats[i] = 0.0;
if (inputBuffer && gAudioIO->mSoftwarePlaythrough) {
float gain = 1.0;
if (gAudioIO->mEmulateMixerInputVol)
gain = gAudioIO->mMixerInputVol;
DoSoftwarePlaythrough(inputBuffer, gAudioIO->mCaptureFormat,
numCaptureChannels,
(float *)outputBuffer, (int)framesPerBuffer, gain);
}
if (gAudioIO->mSeek)
{
gAudioIO->mAudioThreadFillBuffersLoopRunning = false;
while( gAudioIO->mAudioThreadFillBuffersLoopActive == true )
{
wxMilliSleep( 50 );
}
gAudioIO->mTime += gAudioIO->mSeek;
if (gAudioIO->mTime < gAudioIO->mT0)
gAudioIO->mTime = gAudioIO->mT0;
else if (gAudioIO->mTime > gAudioIO->mT1)
gAudioIO->mTime = gAudioIO->mT1;
gAudioIO->mSeek = 0.0;
for (i = 0; i < (unsigned int)numPlaybackTracks; i++)
{
gAudioIO->mPlaybackMixers[i]->Reposition( gAudioIO->mTime );
gAudioIO->mPlaybackBuffers[i]->Discard( gAudioIO->mPlaybackBuffers[i]->AvailForGet() );
}
gAudioIO->mT = gAudioIO->mTime;
gAudioIO->mAudioThreadShouldCallFillBuffersOnce = true;
while( gAudioIO->mAudioThreadShouldCallFillBuffersOnce == true )
{
wxMilliSleep( 50 );
}
gAudioIO->mAudioThreadFillBuffersLoopRunning = true;
return paContinue;
}
int numSolo = 0;
for( t = 0; t < numPlaybackTracks; t++ )
if( gAudioIO->mPlaybackTracks[t]->GetSolo() )
numSolo++;
for( t = 0; t < numPlaybackTracks; t++)
{
WaveTrack *vt = gAudioIO->mPlaybackTracks[t];
if (linkFlag)
linkFlag = false;
else {
cut = false;
if (numSolo>0 && !vt->GetSolo())
cut = true;
if (vt->GetMute() && !vt->GetSolo())
cut = true;
linkFlag = vt->GetLinked();
}
if (cut)
{
gAudioIO->mPlaybackBuffers[t]->Discard(framesPerBuffer);
continue;
}
unsigned int len = (unsigned int)
gAudioIO->mPlaybackBuffers[t]->Get((samplePtr)tempFloats,
floatSample,
(int)framesPerBuffer);
if (len == 0 && gAudioIO->mTime >= gAudioIO->mT1 &&
!gAudioIO->mPlayLooped)
{
#if USE_PORTAUDIO_V19
callbackReturn = paComplete;
#else
callbackReturn = 1;
gAudioIO->mInCallbackFinishedState = true;
#endif
}
if (gAudioIO->mPlayLooped && gAudioIO->mTime >= gAudioIO->mT1)
{
gAudioIO->mTime = gAudioIO->mT0 + (gAudioIO->mTime - gAudioIO->mT1);
}
if (vt->GetChannel() == Track::LeftChannel ||
vt->GetChannel() == Track::MonoChannel)
{
float gain = vt->GetChannelGain(0);
if (gAudioIO->mEmulateMixerOutputVol)
gain *= gAudioIO->mMixerOutputVol;
for(i=0; i<len; i++)
outputFloats[numPlaybackChannels*i] += gain*tempFloats[i];
}
if (vt->GetChannel() == Track::RightChannel ||
vt->GetChannel() == Track::MonoChannel)
{
float gain = vt->GetChannelGain(1);
if (gAudioIO->mEmulateMixerOutputVol)
gain *= gAudioIO->mMixerOutputVol;
for(i=0; i<len; i++)
outputFloats[numPlaybackChannels*i+1] += gain*tempFloats[i];
}
}
for( i = 0; i < framesPerBuffer*numPlaybackChannels; i++)
{
float f = outputFloats[i];
if (f > 1.0)
outputFloats[i] = 1.0;
else if (f < -1.0)
outputFloats[i] = -1.0;
}
}
if( inputBuffer && (numCaptureChannels > 0) )
{
unsigned int len = framesPerBuffer;
for( t = 0; t < numCaptureChannels; t++) {
unsigned int avail =
(unsigned int)gAudioIO->mCaptureBuffers[t]->AvailForPut();
if (avail < len)
len = avail;
}
if (len < framesPerBuffer)
{
gAudioIO->mLostSamples += (framesPerBuffer - len);
wxPrintf(wxT("lost %d samples\n"), (int)(framesPerBuffer - len));
}
float gain = 1.0;
if (gAudioIO->mEmulateMixerInputVol)
gain = gAudioIO->mMixerInputVol;
if (len > 0) {
for( t = 0; t < numCaptureChannels; t++) {
switch(gAudioIO->mCaptureFormat) {
case floatSample: {
float *inputFloats = (float *)inputBuffer;
for( i = 0; i < len; i++)
tempFloats[i] =
inputFloats[numCaptureChannels*i+t] * gain;
} break;
case int24Sample:
wxASSERT(false);
break;
case int16Sample: {
short *inputShorts = (short *)inputBuffer;
short *tempShorts = (short *)tempBuffer;
for( i = 0; i < len; i++) {
float tmp = inputShorts[numCaptureChannels*i+t] * gain;
if (tmp > 32767)
tmp = 32767;
if (tmp < -32768)
tmp = -32768;
tempShorts[i] = (short)(tmp);
}
} break;
}
gAudioIO->mCaptureBuffers[t]->Put((samplePtr)tempBuffer,
gAudioIO->mCaptureFormat,
len);
}
}
}
gAudioIO->mTime += ((framesPerBuffer / gAudioIO->mRate) / gAudioIO->mPlaySpeed);
#if USE_PORTAUDIO_V19
#else
if (numCaptureChannels > 0 && numPlaybackChannels > 0)
gAudioIO->mLastRecordingOffset = (Pa_StreamTime(gAudioIO->mPortStreamV18) - outTime) / gAudioIO->mRate;
else
gAudioIO->mLastRecordingOffset = 0;
#endif
}
else {
if( outputBuffer && (numPlaybackChannels > 0) ) {
float *outputFloats = (float *)outputBuffer;
for( i = 0; i < framesPerBuffer*numPlaybackChannels; i++)
outputFloats[i] = 0.0;
if (inputBuffer && gAudioIO->mSoftwarePlaythrough) {
float gain = 1.0;
if (gAudioIO->mEmulateMixerInputVol)
gain = gAudioIO->mMixerInputVol;
DoSoftwarePlaythrough(inputBuffer, gAudioIO->mCaptureFormat,
numCaptureChannels,
(float *)outputBuffer, (int)framesPerBuffer, gain);
}
}
}
gAudioIO->mUpdatingMeters = true;
if (gAudioIO->mUpdateMeters) {
if (!gAudioIO->mOutputMeter->IsMeterDisabled()
&& gAudioIO->mOutputMeter && outputBuffer)
gAudioIO->mOutputMeter->UpdateDisplay(numPlaybackChannels,
framesPerBuffer,
(float *)outputBuffer);
if (!gAudioIO->mInputMeter->IsMeterDisabled()
&& gAudioIO->mInputMeter && inputBuffer) {
if (gAudioIO->mCaptureFormat == floatSample)
gAudioIO->mInputMeter->UpdateDisplay(numCaptureChannels,
framesPerBuffer,
(float *)inputBuffer);
else {
CopySamples((samplePtr)inputBuffer, gAudioIO->mCaptureFormat,
(samplePtr)tempFloats, floatSample,
framesPerBuffer * numCaptureChannels);
gAudioIO->mInputMeter->UpdateDisplay(numCaptureChannels,
framesPerBuffer,
tempFloats);
}
}
}
gAudioIO->mUpdatingMeters = false;
return callbackReturn;
}