/* * Copyright (C) 2006-2009 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "SoundCardReaderWriter.h" int scrwCallback(const void* input, void* output, unsigned long nSamples, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) { wxASSERT(userData != NULL); CSoundCardReaderWriter* object = reinterpret_cast(userData); object->callback(static_cast(input), static_cast(output), nSamples); return paContinue; } CSoundCardReaderWriter::CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize) : m_readDevice(readDevice), m_writeDevice(writeDevice), m_sampleRate(sampleRate), m_blockSize(blockSize), m_buffer(NULL), m_stream(NULL), m_callback(NULL), m_id(-1) { m_buffer = new wxFloat32[blockSize]; } CSoundCardReaderWriter::~CSoundCardReaderWriter() { delete[] m_buffer; } wxArrayString CSoundCardReaderWriter::getReadDevices() { wxArrayString devices; devices.Alloc(10); PaError error = ::Pa_Initialize(); if (error != paNoError) return devices; #if defined(__WINDOWS__) PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); #elif defined(__APPLE__) && defined(__MACH__) PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); #else PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); #endif if (apiIndex == paHostApiNotFound) { ::Pa_Terminate(); return devices; } PaDeviceIndex n = ::Pa_GetDeviceCount(); if (n <= 0) { ::Pa_Terminate(); return devices; } for (PaDeviceIndex i = 0; i < n; i++) { const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); if (device->hostApi != apiIndex) continue; if (device->maxInputChannels > 0) { wxString name(device->name, wxConvLocal); devices.Add(name); } } ::Pa_Terminate(); return devices; } wxArrayString CSoundCardReaderWriter::getWriteDevices() { wxArrayString devices; devices.Alloc(10); PaError error = ::Pa_Initialize(); if (error != paNoError) return devices; #if defined(__WINDOWS__) PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); #elif defined(__APPLE__) && defined(__MACH__) PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); #else PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); #endif if (apiIndex == paHostApiNotFound) { ::Pa_Terminate(); return devices; } PaDeviceIndex n = ::Pa_GetDeviceCount(); if (n <= 0) { ::Pa_Terminate(); return devices; } for (PaDeviceIndex i = 0; i < n; i++) { const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); if (device->hostApi != apiIndex) continue; if (device->maxOutputChannels > 0) { wxString name(device->name, wxConvLocal); devices.Add(name); } } ::Pa_Terminate(); return devices; } void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id) { wxASSERT(callback != NULL); m_callback = callback; m_id = id; } bool CSoundCardReaderWriter::open() { PaError error = ::Pa_Initialize(); if (error != paNoError) { wxLogError(wxT("Cannot initialise PortAudio")); return false; } PaStreamParameters* pParamsIn = NULL; PaStreamParameters* pParamsOut = NULL; PaStreamParameters paramsIn; PaStreamParameters paramsOut; PaDeviceIndex inDev, outDev; bool res = convertNameToDevices(inDev, outDev); if (!res) { wxLogError(wxT("Cannot convert name to device")); return false; } if (inDev != -1) { const PaDeviceInfo* inInfo = ::Pa_GetDeviceInfo(inDev); if (inInfo == NULL) { wxLogError(wxT("Cannot get device information for the input device")); return false; } paramsIn.device = inDev; paramsIn.channelCount = 1; paramsIn.sampleFormat = paFloat32; paramsIn.hostApiSpecificStreamInfo = NULL; paramsIn.suggestedLatency = inInfo->defaultLowInputLatency; pParamsIn = ¶msIn; } if (outDev != -1) { const PaDeviceInfo* outInfo = ::Pa_GetDeviceInfo(outDev); if (outInfo == NULL) { wxLogError(wxT("Cannot get device information for the output device")); return false; } paramsOut.device = outDev; paramsOut.channelCount = 1; paramsOut.sampleFormat = paFloat32; paramsOut.hostApiSpecificStreamInfo = NULL; paramsOut.suggestedLatency = outInfo->defaultLowOutputLatency; pParamsOut = ¶msOut; } error = ::Pa_OpenStream(&m_stream, pParamsIn, pParamsOut, double(m_sampleRate), m_blockSize, paNoFlag, &scrwCallback, this); if (error != paNoError) { wxLogError(wxT("Cannot open the audios stream(s)")); ::Pa_Terminate(); return false; } error = ::Pa_StartStream(m_stream); if (error != paNoError) { wxLogError(wxT("Cannot start the audio stream(s)")); ::Pa_CloseStream(m_stream); m_stream = NULL; ::Pa_Terminate(); return false; } return true; } void CSoundCardReaderWriter::close() { wxASSERT(m_stream != NULL); ::Pa_AbortStream(m_stream); ::Pa_CloseStream(m_stream); ::Pa_Terminate(); } void CSoundCardReaderWriter::callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples) { if (m_callback != NULL) m_callback->callback(input, output, nSamples, m_id); } bool CSoundCardReaderWriter::convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev) { inDev = outDev = -1; #if defined(__WINDOWS__) PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); #elif defined(__APPLE__) && defined(__MACH__) PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); #else PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); #endif if (apiIndex == paHostApiNotFound) return false; PaDeviceIndex n = ::Pa_GetDeviceCount(); if (n <= 0) return false; for (PaDeviceIndex i = 0; i < n; i++) { const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); if (device->hostApi != apiIndex) continue; wxString name(device->name, wxConvLocal); if (!m_readDevice.IsEmpty() && m_readDevice.IsSameAs(name) && device->maxInputChannels > 0) inDev = i; if (!m_writeDevice.IsEmpty() && m_writeDevice.IsSameAs(name) && device->maxOutputChannels > 0) outDev = i; } if (inDev == -1 && outDev == -1) return false; return true; }