diff options
Diffstat (limited to 'gr-audio/lib')
-rw-r--r-- | gr-audio/lib/Makefile.am | 5 | ||||
-rw-r--r-- | gr-audio/lib/osx/audio_osx.h | 71 | ||||
-rw-r--r-- | gr-audio/lib/osx/audio_osx_sink.cc | 404 | ||||
-rw-r--r-- | gr-audio/lib/osx/audio_osx_sink.h | 79 | ||||
-rw-r--r-- | gr-audio/lib/osx/audio_osx_source.cc | 1016 | ||||
-rw-r--r-- | gr-audio/lib/osx/audio_osx_source.h | 115 | ||||
-rw-r--r-- | gr-audio/lib/windows/audio_windows_sink.cc | 323 | ||||
-rw-r--r-- | gr-audio/lib/windows/audio_windows_sink.h | 72 | ||||
-rw-r--r-- | gr-audio/lib/windows/audio_windows_source.cc | 205 | ||||
-rw-r--r-- | gr-audio/lib/windows/audio_windows_source.h | 56 |
10 files changed, 2342 insertions, 4 deletions
diff --git a/gr-audio/lib/Makefile.am b/gr-audio/lib/Makefile.am index 9972efadc..42a2b867b 100644 --- a/gr-audio/lib/Makefile.am +++ b/gr-audio/lib/Makefile.am @@ -127,11 +127,10 @@ libgnuradio_audio_la_SOURCES += \ osx/audio_osx_sink.cc noinst_HEADERS += \ + osx/audio_osx.h \ osx/audio_osx_source.h \ osx/audio_osx_sink.h -dist_etc_DATA += osx/gr-audio-osx.conf - endif ######################################################################## @@ -177,6 +176,4 @@ noinst_HEADERS += \ windows/audio_windows_source.h \ windows/audio_windows_sink.h -dist_etc_DATA += windows/gr-audio-windows.conf - endif diff --git a/gr-audio/lib/osx/audio_osx.h b/gr-audio/lib/osx/audio_osx.h new file mode 100644 index 000000000..79e79e36c --- /dev/null +++ b/gr-audio/lib/osx/audio_osx.h @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_AUDIO_OSX_H +#define INCLUDED_AUDIO_OSX_H + +#include <iostream> +#include <string.h> + +#define CheckErrorAndThrow(err,what,throw_str) \ + if (err) { \ + OSStatus error = static_cast<OSStatus>(err); \ + char err_str[4]; \ + strncpy (err_str, (char*)(&err), 4); \ + std::cerr << what << std::endl; \ + std::cerr << " Error# " << error << " ('" << err_str \ + << "')" << std::endl; \ + std::cerr << " " << __FILE__ << ":" << __LINE__ << std::endl; \ + fflush (stderr); \ + throw std::runtime_error (throw_str); \ + } + +#define CheckError(err,what) \ + if (err) { \ + OSStatus error = static_cast<OSStatus>(err); \ + char err_str[4]; \ + strncpy (err_str, (char*)(&err), 4); \ + std::cerr << what << std::endl; \ + std::cerr << " Error# " << error << " ('" << err_str \ + << "')" << std::endl; \ + std::cerr << " " << __FILE__ << ":" << __LINE__ << std::endl; \ + fflush (stderr); \ + } + +#ifdef WORDS_BIGENDIAN +#define GR_PCM_ENDIANNESS kLinearPCMFormatFlagIsBigEndian +#else +#define GR_PCM_ENDIANNESS 0 +#endif + +// Check the version of MacOSX being used +#ifdef __APPLE_CC__ +#include <AvailabilityMacros.h> +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 +#define GR_USE_OLD_AUDIO_UNIT +#endif +#endif + +#endif /* INCLUDED_AUDIO_OSX_H */ diff --git a/gr-audio/lib/osx/audio_osx_sink.cc b/gr-audio/lib/osx/audio_osx_sink.cc new file mode 100644 index 000000000..e9b00aa8a --- /dev/null +++ b/gr-audio/lib/osx/audio_osx_sink.cc @@ -0,0 +1,404 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gr_audio_registry.h" +#include <audio_osx_sink.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <audio_osx.h> + +#define _OSX_AU_DEBUG_ 0 + +AUDIO_REGISTER_SINK(REG_PRIO_MED, osx)( + int sampling_rate, const std::string &device_name, bool ok_to_block +){ + return audio_sink::sptr(new audio_osx_sink(sampling_rate, device_name, ok_to_block)); +} + +audio_osx_sink::audio_osx_sink (int sample_rate, + const std::string device_name, + bool do_block, + int channel_config, + int max_sample_count) + : audio_sink ("audio_osx_sink", + gr_make_io_signature (0, 0, 0), + gr_make_io_signature (0, 0, 0)), + d_sample_rate (0.0), d_channel_config (0), d_n_channels (0), + d_queueSampleCount (0), d_max_sample_count (0), + d_do_block (do_block), d_internal (0), d_cond_data (0), + d_OutputAU (0) +{ + if (sample_rate <= 0) { + std::cerr << "Invalid Sample Rate: " << sample_rate << std::endl; + throw std::invalid_argument ("audio_osx_sink::audio_osx_sink"); + } else + d_sample_rate = (Float64) sample_rate; + + if (channel_config <= 0 & channel_config != -1) { + std::cerr << "Invalid Channel Config: " << channel_config << std::endl; + throw std::invalid_argument ("audio_osx_sink::audio_osx_sink"); + } else if (channel_config == -1) { +// no user input; try "device name" instead + int l_n_channels = (int) strtol (device_name.data(), (char **)NULL, 10); + if (l_n_channels == 0 & errno) { + std::cerr << "Error Converting Device Name: " << errno << std::endl; + throw std::invalid_argument ("audio_osx_sink::audio_osx_sink"); + } + if (l_n_channels <= 0) + channel_config = 2; + else + channel_config = l_n_channels; + } + + d_n_channels = d_channel_config = channel_config; + +// set the input signature + + set_input_signature (gr_make_io_signature (1, d_n_channels, sizeof (float))); + +// check that the max # of samples to store is valid + + if (max_sample_count == -1) + max_sample_count = sample_rate; + else if (max_sample_count <= 0) { + std::cerr << "Invalid Max Sample Count: " << max_sample_count << std::endl; + throw std::invalid_argument ("audio_osx_sink::audio_osx_sink"); + } + + d_max_sample_count = max_sample_count; + +// allocate the output circular buffer(s), one per channel + + d_buffers = (circular_buffer<float>**) new + circular_buffer<float>* [d_n_channels]; + UInt32 n_alloc = (UInt32) ceil ((double) d_max_sample_count); + for (UInt32 n = 0; n < d_n_channels; n++) { + d_buffers[n] = new circular_buffer<float> (n_alloc, false, false); + } + +// create the default AudioUnit for output + OSStatus err = noErr; + +// Open the default output unit +#ifndef GR_USE_OLD_AUDIO_UNIT + AudioComponentDescription desc; +#else + ComponentDescription desc; +#endif + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + +#ifndef GR_USE_OLD_AUDIO_UNIT + AudioComponent comp = AudioComponentFindNext(NULL, &desc); + if (comp == NULL) { + std::cerr << "AudioComponentFindNext Error" << std::endl; + throw std::runtime_error ("audio_osx_sink::audio_osx_sink"); + } +#else + Component comp = FindNextComponent (NULL, &desc); + if (comp == NULL) { + std::cerr << "FindNextComponent Error" << std::endl; + throw std::runtime_error ("audio_osx_sink::audio_osx_sink"); + } +#endif + +#ifndef GR_USE_OLD_AUDIO_UNIT + err = AudioComponentInstanceNew (comp, &d_OutputAU); + CheckErrorAndThrow (err, "AudioComponentInstanceNew", "audio_osx_sink::audio_osx_sink"); +#else + err = OpenAComponent (comp, &d_OutputAU); + CheckErrorAndThrow (err, "OpenAComponent", "audio_osx_sink::audio_osx_sink"); +#endif + +// Set up a callback function to generate output to the output unit + + AURenderCallbackStruct input; + input.inputProc = (AURenderCallback)(audio_osx_sink::AUOutputCallback); + input.inputProcRefCon = this; + + err = AudioUnitSetProperty (d_OutputAU, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &input, + sizeof (input)); + CheckErrorAndThrow (err, "AudioUnitSetProperty Render Callback", "audio_osx_sink::audio_osx_sink"); + +// tell the Output Unit what format data will be supplied to it +// so that it handles any format conversions + + AudioStreamBasicDescription streamFormat; + streamFormat.mSampleRate = (Float64)(sample_rate); + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = (kLinearPCMFormatFlagIsFloat | + GR_PCM_ENDIANNESS | + kLinearPCMFormatFlagIsPacked | + kAudioFormatFlagIsNonInterleaved); + streamFormat.mBytesPerPacket = 4; + streamFormat.mFramesPerPacket = 1; + streamFormat.mBytesPerFrame = 4; + streamFormat.mChannelsPerFrame = d_n_channels; + streamFormat.mBitsPerChannel = 32; + + err = AudioUnitSetProperty (d_OutputAU, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &streamFormat, + sizeof (AudioStreamBasicDescription)); + CheckErrorAndThrow (err, "AudioUnitSetProperty StreamFormat", "audio_osx_sink::audio_osx_sink"); + +// create the stuff to regulate I/O + + d_cond_data = new gruel::condition_variable (); + if (d_cond_data == NULL) + CheckErrorAndThrow (errno, "new condition (data)", + "audio_osx_sink::audio_osx_sink"); + + d_internal = new gruel::mutex (); + if (d_internal == NULL) + CheckErrorAndThrow (errno, "new mutex (internal)", + "audio_osx_sink::audio_osx_sink"); + +// initialize the AU for output + + err = AudioUnitInitialize (d_OutputAU); + CheckErrorAndThrow (err, "AudioUnitInitialize", + "audio_osx_sink::audio_osx_sink"); + +#if _OSX_AU_DEBUG_ + std::cerr << "audio_osx_sink Parameters:" << std::endl; + std::cerr << " Sample Rate is " << d_sample_rate << std::endl; + std::cerr << " Number of Channels is " << d_n_channels << std::endl; + std::cerr << " Max # samples to store per channel is " << d_max_sample_count << std::endl; +#endif +} + +bool audio_osx_sink::IsRunning () +{ + UInt32 AURunning = 0, AUSize = sizeof (UInt32); + + OSStatus err = AudioUnitGetProperty (d_OutputAU, + kAudioOutputUnitProperty_IsRunning, + kAudioUnitScope_Global, + 0, + &AURunning, + &AUSize); + CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning", + "audio_osx_sink::IsRunning"); + + return (AURunning); +} + +bool audio_osx_sink::start () +{ + if (! IsRunning ()) { + OSStatus err = AudioOutputUnitStart (d_OutputAU); + CheckErrorAndThrow (err, "AudioOutputUnitStart", "audio_osx_sink::start"); + } + + return (true); +} + +bool audio_osx_sink::stop () +{ + if (IsRunning ()) { + OSStatus err = AudioOutputUnitStop (d_OutputAU); + CheckErrorAndThrow (err, "AudioOutputUnitStop", "audio_osx_sink::stop"); + + for (UInt32 n = 0; n < d_n_channels; n++) { + d_buffers[n]->abort (); + } + } + + return (true); +} + +audio_osx_sink::~audio_osx_sink () +{ +// stop and close the AudioUnit + stop (); + AudioUnitUninitialize (d_OutputAU); +#ifndef GR_USE_OLD_AUDIO_UNIT + AudioComponentInstanceDispose (d_OutputAU); +#else + CloseComponent (d_OutputAU); +#endif + +// empty and delete the queues + for (UInt32 n = 0; n < d_n_channels; n++) { + delete d_buffers[n]; + d_buffers[n] = 0; + } + delete [] d_buffers; + d_buffers = 0; + +// close and delete control stuff + delete d_cond_data; + d_cond_data = 0; + delete d_internal; + d_internal = 0; +} + +int +audio_osx_sink::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gruel::scoped_lock l (*d_internal); + + /* take the input data, copy it, and push it to the bottom of the queue + mono input are pushed onto queue[0]; + stereo input are pushed onto queue[1]. + Start the AudioUnit if necessary. */ + + UInt32 l_max_count; + int diff_count = d_max_sample_count - noutput_items; + if (diff_count < 0) + l_max_count = 0; + else + l_max_count = (UInt32) diff_count; + +#if 0 + if (l_max_count < d_queueItemLength->back()) { +// allow 2 buffers at a time, regardless of length + l_max_count = d_queueItemLength->back(); + } +#endif + +#if _OSX_AU_DEBUG_ + std::cerr << "work1: qSC = " << d_queueSampleCount << ", lMC = "<< l_max_count + << ", dmSC = " << d_max_sample_count << ", nOI = " << noutput_items << std::endl; +#endif + + if (d_queueSampleCount > l_max_count) { +// data coming in too fast; do_block decides what to do + if (d_do_block == true) { +// block until there is data to return + while (d_queueSampleCount > l_max_count) { +// release control so-as to allow data to be retrieved; +// block until there is data to return + d_cond_data->wait (l); +// the condition's 'notify' was called; acquire control +// to keep thread safe + } + } + } +// not blocking case and overflow is handled by the circular buffer + +// add the input frames to the buffers' queue, checking for overflow + + UInt32 l_counter; + int res = 0; + float* inBuffer = (float*) input_items[0]; + const UInt32 l_size = input_items.size(); + for (l_counter = 0; l_counter < l_size; l_counter++) { + inBuffer = (float*) input_items[l_counter]; + int l_res = d_buffers[l_counter]->enqueue (inBuffer, + noutput_items); + if (l_res == -1) + res = -1; + } + while (l_counter < d_n_channels) { +// for extra channels, copy the last input's data + int l_res = d_buffers[l_counter++]->enqueue (inBuffer, + noutput_items); + if (l_res == -1) + res = -1; + } + + if (res == -1) { +// data coming in too fast +// drop oldest buffer + fputs ("aO", stderr); + fflush (stderr); +// set the local number of samples available to the max + d_queueSampleCount = d_buffers[0]->buffer_length_items (); + } else { +// keep up the local sample count + d_queueSampleCount += noutput_items; + } + +#if _OSX_AU_DEBUG_ + std::cerr << "work2: #OI = " << noutput_items << ", #Cnt = " + << d_queueSampleCount << ", mSC = " << d_max_sample_count << std::endl; +#endif + + return (noutput_items); +} + +OSStatus audio_osx_sink::AUOutputCallback +(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + audio_osx_sink* This = (audio_osx_sink*) inRefCon; + OSStatus err = noErr; + + gruel::scoped_lock l (*This->d_internal); + +#if _OSX_AU_DEBUG_ + std::cerr << "cb_in: SC = " << This->d_queueSampleCount + << ", in#F = " << inNumberFrames << std::endl; +#endif + + if (This->d_queueSampleCount < inNumberFrames) { +// not enough data to fill request + err = -1; + } else { +// enough data; remove data from our buffers into the AU's buffers + int l_counter = This->d_n_channels; + + while (--l_counter >= 0) { + size_t t_n_output_items = inNumberFrames; + float* outBuffer = (float*) ioData->mBuffers[l_counter].mData; + This->d_buffers[l_counter]->dequeue (outBuffer, &t_n_output_items); + if (t_n_output_items != inNumberFrames) { + throw std::runtime_error ("audio_osx_sink::AUOutputCallback(): " + "number of available items changing " + "unexpectedly.\n"); + } + } + + This->d_queueSampleCount -= inNumberFrames; + } + +#if _OSX_AU_DEBUG_ + std::cerr << "cb_out: SC = " << This->d_queueSampleCount << std::endl; +#endif + +// signal that data is available + This->d_cond_data->notify_one (); + + return (err); +} diff --git a/gr-audio/lib/osx/audio_osx_sink.h b/gr-audio/lib/osx/audio_osx_sink.h new file mode 100644 index 000000000..13bd95d53 --- /dev/null +++ b/gr-audio/lib/osx/audio_osx_sink.h @@ -0,0 +1,79 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_AUDIO_OSX_SINK_H +#define INCLUDED_AUDIO_OSX_SINK_H + +#include <gr_audio_sink.h> +#include <string> +#include <list> +#include <AudioUnit/AudioUnit.h> +#include <circular_buffer.h> + +/*! + * \brief audio sink using OSX + * + * input signature is one or two streams of floats. + * Input samples must be in the range [-1,1]. + */ + +class audio_osx_sink : public audio_sink { + + Float64 d_sample_rate; + int d_channel_config; + UInt32 d_n_channels; + UInt32 d_queueSampleCount, d_max_sample_count; + bool d_do_block; + gruel::mutex* d_internal; + gruel::condition_variable* d_cond_data; + circular_buffer<float>** d_buffers; + +// AudioUnits and Such + AudioUnit d_OutputAU; + +public: + audio_osx_sink (int sample_rate = 44100, + const std::string device_name = "2", + bool do_block = true, + int channel_config = -1, + int max_sample_count = -1); + + ~audio_osx_sink (); + + bool IsRunning (); + bool start (); + bool stop (); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +private: + static OSStatus AUOutputCallback (void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); +}; + +#endif /* INCLUDED_AUDIO_OSX_SINK_H */ diff --git a/gr-audio/lib/osx/audio_osx_source.cc b/gr-audio/lib/osx/audio_osx_source.cc new file mode 100644 index 000000000..d4a377033 --- /dev/null +++ b/gr-audio/lib/osx/audio_osx_source.cc @@ -0,0 +1,1016 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gr_audio_registry.h" +#include <audio_osx_source.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <audio_osx.h> + +#define _OSX_AU_DEBUG_ 0 +#define _OSX_DO_LISTENERS_ 0 + +AUDIO_REGISTER_SOURCE(REG_PRIO_MED, osx)( + int sampling_rate, const std::string &device_name, bool ok_to_block +){ + return audio_source::sptr(new audio_osx_source(sampling_rate, device_name, ok_to_block)); +} + +void PrintStreamDesc (AudioStreamBasicDescription *inDesc) +{ + if (inDesc == NULL) { + std::cerr << "PrintStreamDesc: Can't print a NULL desc!" << std::endl; + return; + } + + std::cerr << " Sample Rate : " << inDesc->mSampleRate << std::endl; + char format_id[4]; + strncpy (format_id, (char*)(&inDesc->mFormatID), 4); + std::cerr << " Format ID : " << format_id << std::endl; + std::cerr << " Format Flags : " << inDesc->mFormatFlags << std::endl; + std::cerr << " Bytes per Packet : " << inDesc->mBytesPerPacket << std::endl; + std::cerr << " Frames per Packet : " << inDesc->mFramesPerPacket << std::endl; + std::cerr << " Bytes per Frame : " << inDesc->mBytesPerFrame << std::endl; + std::cerr << " Channels per Frame : " << inDesc->mChannelsPerFrame << std::endl; + std::cerr << " Bits per Channel : " << inDesc->mBitsPerChannel << std::endl; +} + +// FIXME these should query some kind of user preference + +audio_osx_source::audio_osx_source (int sample_rate, + const std::string device_name, + bool do_block, + int channel_config, + int max_sample_count) + : audio_source ("audio_osx_source", + gr_make_io_signature (0, 0, 0), + gr_make_io_signature (0, 0, 0)), + d_deviceSampleRate (0.0), d_outputSampleRate (0.0), + d_channel_config (0), + d_inputBufferSizeFrames (0), d_inputBufferSizeBytes (0), + d_outputBufferSizeFrames (0), d_outputBufferSizeBytes (0), + d_deviceBufferSizeFrames (0), d_deviceBufferSizeBytes (0), + d_leadSizeFrames (0), d_leadSizeBytes (0), + d_trailSizeFrames (0), d_trailSizeBytes (0), + d_extraBufferSizeFrames (0), d_extraBufferSizeBytes (0), + d_queueSampleCount (0), d_max_sample_count (0), + d_n_AvailableInputFrames (0), d_n_ActualInputFrames (0), + d_n_user_channels (0), d_n_max_channels (0), d_n_deviceChannels (0), + d_do_block (do_block), d_passThrough (false), + d_internal (0), d_cond_data (0), + d_buffers (0), + d_InputAU (0), d_InputBuffer (0), d_OutputBuffer (0), + d_AudioConverter (0) +{ + if (sample_rate <= 0) { + std::cerr << "Invalid Sample Rate: " << sample_rate << std::endl; + throw std::invalid_argument ("audio_osx_source::audio_osx_source"); + } else + d_outputSampleRate = (Float64) sample_rate; + + if (channel_config <= 0 & channel_config != -1) { + std::cerr << "Invalid Channel Config: " << channel_config << std::endl; + throw std::invalid_argument ("audio_osx_source::audio_osx_source"); + } else if (channel_config == -1) { +// no user input; try "device name" instead + int l_n_channels = (int) strtol (device_name.data(), (char **)NULL, 10); + if (l_n_channels == 0 & errno) { + std::cerr << "Error Converting Device Name: " << errno << std::endl; + throw std::invalid_argument ("audio_osx_source::audio_osx_source"); + } + if (l_n_channels <= 0) + channel_config = 2; + else + channel_config = l_n_channels; + } + + d_channel_config = channel_config; + +// check that the max # of samples to store is valid + + if (max_sample_count == -1) + max_sample_count = sample_rate; + else if (max_sample_count <= 0) { + std::cerr << "Invalid Max Sample Count: " << max_sample_count << std::endl; + throw std::invalid_argument ("audio_osx_source::audio_osx_source"); + } + + d_max_sample_count = max_sample_count; + +#if _OSX_AU_DEBUG_ + std::cerr << "source(): max # samples = " << d_max_sample_count << std::endl; +#endif + + OSStatus err = noErr; + +// create the default AudioUnit for input + +// Open the default input unit +#ifndef GR_USE_OLD_AUDIO_UNIT + AudioComponentDescription InputDesc; +#else + ComponentDescription InputDesc; +#endif + + + InputDesc.componentType = kAudioUnitType_Output; + InputDesc.componentSubType = kAudioUnitSubType_HALOutput; + InputDesc.componentManufacturer = kAudioUnitManufacturer_Apple; + InputDesc.componentFlags = 0; + InputDesc.componentFlagsMask = 0; + +#ifndef GR_USE_OLD_AUDIO_UNIT + AudioComponent comp = AudioComponentFindNext (NULL, &InputDesc); +#else + Component comp = FindNextComponent (NULL, &InputDesc); +#endif + + if (comp == NULL) { +#ifndef GR_USE_OLD_AUDIO_UNIT + std::cerr << "AudioComponentFindNext Error" << std::endl; +#else + std::cerr << "FindNextComponent Error" << std::endl; +#endif + throw std::runtime_error ("audio_osx_source::audio_osx_source"); + } + +#ifndef GR_USE_OLD_AUDIO_UNIT + err = AudioComponentInstanceNew (comp, &d_InputAU); + CheckErrorAndThrow (err, "AudioComponentInstanceNew", + "audio_osx_source::audio_osx_source"); +#else + err = OpenAComponent (comp, &d_InputAU); + CheckErrorAndThrow (err, "OpenAComponent", + "audio_osx_source::audio_osx_source"); +#endif + + + UInt32 enableIO; + +// must enable the AUHAL for input and disable output +// before setting the AUHAL's current device + +// Enable input on the AUHAL + enableIO = 1; + err = AudioUnitSetProperty (d_InputAU, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, + 1, // input element + &enableIO, + sizeof (UInt32)); + CheckErrorAndThrow (err, "AudioUnitSetProperty Input Enable", + "audio_osx_source::audio_osx_source"); + +// Disable output on the AUHAL + enableIO = 0; + err = AudioUnitSetProperty (d_InputAU, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, + 0, // output element + &enableIO, + sizeof (UInt32)); + CheckErrorAndThrow (err, "AudioUnitSetProperty Output Disable", + "audio_osx_source::audio_osx_source"); + +// set the default input device for our input AU + + SetDefaultInputDeviceAsCurrent (); + +#if _OSX_DO_LISTENERS_ +// set up a listener if default hardware input device changes + + err = AudioHardwareAddPropertyListener + (kAudioHardwarePropertyDefaultInputDevice, + (AudioHardwarePropertyListenerProc) HardwareListener, + this); + + CheckErrorAndThrow (err, "AudioHardwareAddPropertyListener", + "audio_osx_source::audio_osx_source"); + +// Add a listener for any changes in the input AU's output stream +// the function "UnitListener" will be called if the stream format +// changes for whatever reason + + err = AudioUnitAddPropertyListener + (d_InputAU, + kAudioUnitProperty_StreamFormat, + (AudioUnitPropertyListenerProc) UnitListener, + this); + CheckErrorAndThrow (err, "Adding Unit Property Listener", + "audio_osx_source::audio_osx_source"); +#endif + +// Now find out if it actually can do input. + + UInt32 hasInput = 0; + UInt32 dataSize = sizeof (hasInput); + err = AudioUnitGetProperty (d_InputAU, + kAudioOutputUnitProperty_HasIO, + kAudioUnitScope_Input, + 1, + &hasInput, + &dataSize); + CheckErrorAndThrow (err, "AudioUnitGetProperty HasIO", + "audio_osx_source::audio_osx_source"); + if (hasInput == 0) { + std::cerr << "Selected Audio Device does not support Input." << std::endl; + throw std::runtime_error ("audio_osx_source::audio_osx_source"); + } + +// Set up a callback function to retrieve input from the Audio Device + + AURenderCallbackStruct AUCallBack; + + AUCallBack.inputProc = (AURenderCallback)(audio_osx_source::AUInputCallback); + AUCallBack.inputProcRefCon = this; + + err = AudioUnitSetProperty (d_InputAU, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, + 0, + &AUCallBack, + sizeof (AURenderCallbackStruct)); + CheckErrorAndThrow (err, "AudioUnitSetProperty Input Callback", + "audio_osx_source::audio_osx_source"); + + UInt32 propertySize; + AudioStreamBasicDescription asbd_device, asbd_client, asbd_user; + +// asbd_device: ASBD of the device that is creating the input data stream +// asbd_client: ASBD of the client size (output) of the hardware device +// asbd_user: ASBD of the user's arguments + +// Get the Stream Format (device side) + + propertySize = sizeof (asbd_device); + err = AudioUnitGetProperty (d_InputAU, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 1, + &asbd_device, + &propertySize); + CheckErrorAndThrow (err, "AudioUnitGetProperty Device Input Stream Format", + "audio_osx_source::audio_osx_source"); + +#if _OSX_AU_DEBUG_ + std::cerr << std::endl << "---- Device Stream Format ----" << std::endl; + PrintStreamDesc (&asbd_device); +#endif + +// Get the Stream Format (client side) + propertySize = sizeof (asbd_client); + err = AudioUnitGetProperty (d_InputAU, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &asbd_client, + &propertySize); + CheckErrorAndThrow (err, "AudioUnitGetProperty Device Ouput Stream Format", + "audio_osx_source::audio_osx_source"); + +#if _OSX_AU_DEBUG_ + std::cerr << std::endl << "---- Client Stream Format ----" << std::endl; + PrintStreamDesc (&asbd_client); +#endif + +// Set the format of all the AUs to the input/output devices channel count + +// get the max number of input (& thus output) channels supported by +// this device + d_n_max_channels = asbd_client.mChannelsPerFrame; + +// create the output io signature; +// no input siganture to set (source is hardware) + set_output_signature (gr_make_io_signature (1, + d_n_max_channels, + sizeof (float))); + +// allocate the output circular buffer(s), one per channel + d_buffers = (circular_buffer<float>**) new + circular_buffer<float>* [d_n_max_channels]; + UInt32 n_alloc = (UInt32) ceil ((double) d_max_sample_count); + for (UInt32 n = 0; n < d_n_max_channels; n++) { + d_buffers[n] = new circular_buffer<float> (n_alloc, false, false); + } + + d_deviceSampleRate = asbd_device.mSampleRate; + d_n_deviceChannels = asbd_device.mChannelsPerFrame; + +// create an ASBD for the user's wants + + asbd_user.mSampleRate = d_outputSampleRate; + asbd_user.mFormatID = kAudioFormatLinearPCM; + asbd_user.mFormatFlags = (kLinearPCMFormatFlagIsFloat | + GR_PCM_ENDIANNESS | + kLinearPCMFormatFlagIsPacked | + kAudioFormatFlagIsNonInterleaved); + asbd_user.mBytesPerPacket = 4; + asbd_user.mFramesPerPacket = 1; + asbd_user.mBytesPerFrame = 4; + asbd_user.mChannelsPerFrame = d_n_max_channels; + asbd_user.mBitsPerChannel = 32; + + if (d_deviceSampleRate == d_outputSampleRate) { +// no need to do conversion if asbd_client matches user wants + d_passThrough = true; + d_leadSizeFrames = d_trailSizeFrames = 0L; + } else { + d_passThrough = false; +// Create the audio converter + + err = AudioConverterNew (&asbd_client, &asbd_user, &d_AudioConverter); + CheckErrorAndThrow (err, "AudioConverterNew", + "audio_osx_source::audio_osx_source"); + +// Set the audio converter sample rate quality to "max" ... +// requires more samples, but should sound nicer + + UInt32 ACQuality = kAudioConverterQuality_Max; + propertySize = sizeof (ACQuality); + err = AudioConverterSetProperty (d_AudioConverter, + kAudioConverterSampleRateConverterQuality, + propertySize, + &ACQuality); + CheckErrorAndThrow (err, "AudioConverterSetProperty " + "SampleRateConverterQuality", + "audio_osx_source::audio_osx_source"); + +// set the audio converter's prime method to "pre", +// which uses both leading and trailing frames +// from the "current input". All of this is handled +// internally by the AudioConverter; we just supply +// the frames for conversion. + +// UInt32 ACPrimeMethod = kConverterPrimeMethod_None; + UInt32 ACPrimeMethod = kConverterPrimeMethod_Pre; + propertySize = sizeof (ACPrimeMethod); + err = AudioConverterSetProperty (d_AudioConverter, + kAudioConverterPrimeMethod, + propertySize, + &ACPrimeMethod); + CheckErrorAndThrow (err, "AudioConverterSetProperty PrimeMethod", + "audio_osx_source::audio_osx_source"); + +// Get the size of the I/O buffer(s) to allow for pre-allocated buffers + +// lead frame info (trail frame info is ignored) + + AudioConverterPrimeInfo ACPrimeInfo = {0, 0}; + propertySize = sizeof (ACPrimeInfo); + err = AudioConverterGetProperty (d_AudioConverter, + kAudioConverterPrimeInfo, + &propertySize, + &ACPrimeInfo); + CheckErrorAndThrow (err, "AudioConverterGetProperty PrimeInfo", + "audio_osx_source::audio_osx_source"); + + switch (ACPrimeMethod) { + case (kConverterPrimeMethod_None): + d_leadSizeFrames = + d_trailSizeFrames = 0L; + break; + case (kConverterPrimeMethod_Normal): + d_leadSizeFrames = 0L; + d_trailSizeFrames = ACPrimeInfo.trailingFrames; + break; + default: + d_leadSizeFrames = ACPrimeInfo.leadingFrames; + d_trailSizeFrames = ACPrimeInfo.trailingFrames; + } + } + d_leadSizeBytes = d_leadSizeFrames * sizeof (Float32); + d_trailSizeBytes = d_trailSizeFrames * sizeof (Float32); + + propertySize = sizeof (d_deviceBufferSizeFrames); + err = AudioUnitGetProperty (d_InputAU, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0, + &d_deviceBufferSizeFrames, + &propertySize); + CheckErrorAndThrow (err, "AudioUnitGetProperty Buffer Frame Size", + "audio_osx_source::audio_osx_source"); + + d_deviceBufferSizeBytes = d_deviceBufferSizeFrames * sizeof (Float32); + d_inputBufferSizeBytes = d_deviceBufferSizeBytes + d_leadSizeBytes; + d_inputBufferSizeFrames = d_deviceBufferSizeFrames + d_leadSizeFrames; + +// outBufSizeBytes = floor (inBufSizeBytes * rate_out / rate_in) +// since this is rarely exact, we need another buffer to hold +// "extra" samples not processed at any given sampling period +// this buffer must be at least 4 floats in size, but generally +// follows the rule that +// extraBufSize = ceil (rate_in / rate_out)*sizeof(float) + + d_extraBufferSizeFrames = ((UInt32) ceil (d_deviceSampleRate + / d_outputSampleRate) + * sizeof (float)); + if (d_extraBufferSizeFrames < 4) + d_extraBufferSizeFrames = 4; + d_extraBufferSizeBytes = d_extraBufferSizeFrames * sizeof (float); + + d_outputBufferSizeFrames = (UInt32) ceil (((Float64) d_inputBufferSizeFrames) + * d_outputSampleRate + / d_deviceSampleRate); + d_outputBufferSizeBytes = d_outputBufferSizeFrames * sizeof (float); + d_inputBufferSizeFrames += d_extraBufferSizeFrames; + +// pre-alloc all buffers + + AllocAudioBufferList (&d_InputBuffer, d_n_deviceChannels, + d_inputBufferSizeBytes); + if (d_passThrough == false) { + AllocAudioBufferList (&d_OutputBuffer, d_n_max_channels, + d_outputBufferSizeBytes); + } else { + d_OutputBuffer = d_InputBuffer; + } + +// create the stuff to regulate I/O + + d_cond_data = new gruel::condition_variable (); + if (d_cond_data == NULL) + CheckErrorAndThrow (errno, "new condition (data)", + "audio_osx_source::audio_osx_source"); + + d_internal = new gruel::mutex (); + if (d_internal == NULL) + CheckErrorAndThrow (errno, "new mutex (internal)", + "audio_osx_source::audio_osx_source"); + +// initialize the AU for input + + err = AudioUnitInitialize (d_InputAU); + CheckErrorAndThrow (err, "AudioUnitInitialize", + "audio_osx_source::audio_osx_source"); + +#if _OSX_AU_DEBUG_ + std::cerr << "audio_osx_source Parameters:" << std::endl; + std::cerr << " Device Sample Rate is " << d_deviceSampleRate << std::endl; + std::cerr << " User Sample Rate is " << d_outputSampleRate << std::endl; + std::cerr << " Max Sample Count is " << d_max_sample_count << std::endl; + std::cerr << " # Device Channels is " << d_n_deviceChannels << std::endl; + std::cerr << " # Max Channels is " << d_n_max_channels << std::endl; + std::cerr << " Device Buffer Size is Frames = " << d_deviceBufferSizeFrames << std::endl; + std::cerr << " Lead Size is Frames = " << d_leadSizeFrames << std::endl; + std::cerr << " Trail Size is Frames = " << d_trailSizeFrames << std::endl; + std::cerr << " Input Buffer Size is Frames = " << d_inputBufferSizeFrames << std::endl; + std::cerr << " Output Buffer Size is Frames = " << d_outputBufferSizeFrames << std::endl; +#endif +} + +void +audio_osx_source::AllocAudioBufferList (AudioBufferList** t_ABL, + UInt32 n_channels, + UInt32 bufferSizeBytes) +{ + FreeAudioBufferList (t_ABL); + UInt32 propertySize = (offsetof (AudioBufferList, mBuffers[0]) + + (sizeof (AudioBuffer) * n_channels)); + *t_ABL = (AudioBufferList*) calloc (1, propertySize); + (*t_ABL)->mNumberBuffers = n_channels; + + int counter = n_channels; + + while (--counter >= 0) { + (*t_ABL)->mBuffers[counter].mNumberChannels = 1; + (*t_ABL)->mBuffers[counter].mDataByteSize = bufferSizeBytes; + (*t_ABL)->mBuffers[counter].mData = calloc (1, bufferSizeBytes); + } +} + +void +audio_osx_source::FreeAudioBufferList (AudioBufferList** t_ABL) +{ +// free pre-allocated audio buffer, if it exists + if (*t_ABL != NULL) { + int counter = (*t_ABL)->mNumberBuffers; + while (--counter >= 0) + free ((*t_ABL)->mBuffers[counter].mData); + free (*t_ABL); + (*t_ABL) = 0; + } +} + +bool audio_osx_source::IsRunning () +{ + UInt32 AURunning = 0, AUSize = sizeof (UInt32); + + OSStatus err = AudioUnitGetProperty (d_InputAU, + kAudioOutputUnitProperty_IsRunning, + kAudioUnitScope_Global, + 0, + &AURunning, + &AUSize); + CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning", + "audio_osx_source::IsRunning"); + + return (AURunning); +} + +bool audio_osx_source::start () +{ + if (! IsRunning ()) { + OSStatus err = AudioOutputUnitStart (d_InputAU); + CheckErrorAndThrow (err, "AudioOutputUnitStart", + "audio_osx_source::start"); + } + + return (true); +} + +bool audio_osx_source::stop () +{ + if (IsRunning ()) { + OSStatus err = AudioOutputUnitStop (d_InputAU); + CheckErrorAndThrow (err, "AudioOutputUnitStart", + "audio_osx_source::stop"); + for (UInt32 n = 0; n < d_n_user_channels; n++) { + d_buffers[n]->abort (); + } + } + + return (true); +} + +audio_osx_source::~audio_osx_source () +{ + OSStatus err = noErr; + +// stop the AudioUnit + stop(); + +#if _OSX_DO_LISTENERS_ +// remove the listeners + + err = AudioUnitRemovePropertyListener + (d_InputAU, + kAudioUnitProperty_StreamFormat, + (AudioUnitPropertyListenerProc) UnitListener); + CheckError (err, "~audio_osx_source: AudioUnitRemovePropertyListener"); + + err = AudioHardwareRemovePropertyListener + (kAudioHardwarePropertyDefaultInputDevice, + (AudioHardwarePropertyListenerProc) HardwareListener); + CheckError (err, "~audio_osx_source: AudioHardwareRemovePropertyListener"); +#endif + +// free pre-allocated audio buffers + FreeAudioBufferList (&d_InputBuffer); + + if (d_passThrough == false) { + err = AudioConverterDispose (d_AudioConverter); + CheckError (err, "~audio_osx_source: AudioConverterDispose"); + FreeAudioBufferList (&d_OutputBuffer); + } + +// remove the audio unit + err = AudioUnitUninitialize (d_InputAU); + CheckError (err, "~audio_osx_source: AudioUnitUninitialize"); + +#ifndef GR_USE_OLD_AUDIO_UNIT + err = AudioComponentInstanceDispose (d_InputAU); + CheckError (err, "~audio_osx_source: AudioComponentInstanceDispose"); +#else + err = CloseComponent (d_InputAU); + CheckError (err, "~audio_osx_source: CloseComponent"); +#endif + +// empty and delete the queues + for (UInt32 n = 0; n < d_n_max_channels; n++) { + delete d_buffers[n]; + d_buffers[n] = 0; + } + delete [] d_buffers; + d_buffers = 0; + +// close and delete the control stuff + delete d_cond_data; + d_cond_data = 0; + delete d_internal; + d_internal = 0; +} + +bool +audio_osx_source::check_topology (int ninputs, int noutputs) +{ +// check # inputs to make sure it's valid + if (ninputs != 0) { + std::cerr << "audio_osx_source::check_topology(): number of input " + << "streams provided (" << ninputs + << ") should be 0." << std::endl; + throw std::runtime_error ("audio_osx_source::check_topology()"); + } + +// check # outputs to make sure it's valid + if ((noutputs < 1) | (noutputs > (int) d_n_max_channels)) { + std::cerr << "audio_osx_source::check_topology(): number of output " + << "streams provided (" << noutputs << ") should be in [1," + << d_n_max_channels << "] for the selected audio device." + << std::endl; + throw std::runtime_error ("audio_osx_source::check_topology()"); + } + +// save the actual number of output (user) channels + d_n_user_channels = noutputs; + +#if _OSX_AU_DEBUG_ + std::cerr << "chk_topo: Actual # user output channels = " + << noutputs << std::endl; +#endif + + return (true); +} + +int +audio_osx_source::work +(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + // acquire control to do processing here only + gruel::scoped_lock l (*d_internal); + +#if _OSX_AU_DEBUG_ + std::cerr << "work1: SC = " << d_queueSampleCount + << ", #OI = " << noutput_items + << ", #Chan = " << output_items.size() << std::endl; +#endif + + // set the actual # of output items to the 'desired' amount then + // verify that data is available; if not enough data is available, + // either wait until it is (is "do_block" is true), return (0) is no + // data is available and "do_block" is false, or process the actual + // amount of available data. + + UInt32 actual_noutput_items = noutput_items; + + if (d_queueSampleCount < actual_noutput_items) { + if (d_queueSampleCount == 0) { + // no data; do_block decides what to do + if (d_do_block == true) { + while (d_queueSampleCount == 0) { + // release control so-as to allow data to be retrieved; + // block until there is data to return + d_cond_data->wait (l); + // the condition's 'notify' was called; acquire control to + // keep thread safe + } + } else { + // no data & not blocking; return nothing + return (0); + } + } + // use the actual amount of available data + actual_noutput_items = d_queueSampleCount; + } + + // number of channels + int l_counter = (int) output_items.size(); + + // copy the items from the circular buffer(s) to 'work's output buffers + // verify that the number copied out is as expected. + + while (--l_counter >= 0) { + size_t t_n_output_items = actual_noutput_items; + d_buffers[l_counter]->dequeue ((float*) output_items[l_counter], + &t_n_output_items); + if (t_n_output_items != actual_noutput_items) { + std::cerr << "audio_osx_source::work(): ERROR: number of " + << "available items changing unexpectedly; expecting " + << actual_noutput_items << ", got " + << t_n_output_items << "." << std::endl; + throw std::runtime_error ("audio_osx_source::work()"); + } + } + + // subtract the actual number of items removed from the buffer(s) + // from the local accounting of the number of available samples + + d_queueSampleCount -= actual_noutput_items; + +#if _OSX_AU_DEBUG_ + std::cerr << "work2: SC = " << d_queueSampleCount + << ", act#OI = " << actual_noutput_items << std::endl + << "Returning." << std::endl; +#endif + + return (actual_noutput_items); +} + +OSStatus +audio_osx_source::ConverterCallback +(AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** ioASPD, + void* inUserData) +{ + // take current device buffers and copy them to the tail of the + // input buffers the lead buffer is already there in the first + // d_leadSizeFrames slots + + audio_osx_source* This = static_cast<audio_osx_source*>(inUserData); + AudioBufferList* l_inputABL = This->d_InputBuffer; + UInt32 totalInputBufferSizeBytes = ((*ioNumberDataPackets) * sizeof (float)); + int counter = This->d_n_deviceChannels; + ioData->mNumberBuffers = This->d_n_deviceChannels; + This->d_n_ActualInputFrames = (*ioNumberDataPackets); + +#if _OSX_AU_DEBUG_ + std::cerr << "cc1: io#DP = " << (*ioNumberDataPackets) + << ", TIBSB = " << totalInputBufferSizeBytes + << ", #C = " << counter << std::endl; +#endif + + while (--counter >= 0) { + AudioBuffer* l_ioD_AB = &(ioData->mBuffers[counter]); + l_ioD_AB->mNumberChannels = 1; + l_ioD_AB->mData = (float*)(l_inputABL->mBuffers[counter].mData); + l_ioD_AB->mDataByteSize = totalInputBufferSizeBytes; + } + +#if _OSX_AU_DEBUG_ + std::cerr << "cc2: Returning." << std::endl; +#endif + + return (noErr); +} + +OSStatus +audio_osx_source::AUInputCallback (void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + OSStatus err = noErr; + audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon); + + gruel::scoped_lock l (*This->d_internal); + +#if _OSX_AU_DEBUG_ + std::cerr << "cb0: in#F = " << inNumberFrames + << ", inBN = " << inBusNumber + << ", SC = " << This->d_queueSampleCount << std::endl; +#endif + +// Get the new audio data from the input device + + err = AudioUnitRender (This->d_InputAU, + ioActionFlags, + inTimeStamp, + 1, //inBusNumber, + inNumberFrames, + This->d_InputBuffer); + CheckErrorAndThrow (err, "AudioUnitRender", + "audio_osx_source::AUInputCallback"); + + UInt32 AvailableInputFrames = inNumberFrames; + This->d_n_AvailableInputFrames = inNumberFrames; + +// get the number of actual output frames, +// either via converting the buffer or not + + UInt32 ActualOutputFrames; + + if (This->d_passThrough == true) { + ActualOutputFrames = AvailableInputFrames; + } else { + UInt32 AvailableInputBytes = AvailableInputFrames * sizeof (float); + UInt32 AvailableOutputBytes = AvailableInputBytes; + UInt32 AvailableOutputFrames = AvailableOutputBytes / sizeof (float); + UInt32 propertySize = sizeof (AvailableOutputBytes); + err = AudioConverterGetProperty (This->d_AudioConverter, + kAudioConverterPropertyCalculateOutputBufferSize, + &propertySize, + &AvailableOutputBytes); + CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateOutputBufferSize", "audio_osx_source::audio_osx_source"); + + AvailableOutputFrames = AvailableOutputBytes / sizeof (float); + +#if 0 +// when decimating too much, the output sounds warbly due to +// fluctuating # of output frames +// This should not be a surprise, but there's probably some +// clever programming that could lessed the effect ... +// like finding the "ideal" # of output frames, and keeping +// that number constant no matter the # of input frames + UInt32 l_InputBytes = AvailableOutputBytes; + propertySize = sizeof (AvailableOutputBytes); + err = AudioConverterGetProperty (This->d_AudioConverter, + kAudioConverterPropertyCalculateInputBufferSize, + &propertySize, + &l_InputBytes); + CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateInputBufferSize", "audio_osx_source::audio_osx_source"); + + if (l_InputBytes < AvailableInputBytes) { +// OK to zero pad the input a little + AvailableOutputFrames += 1; + AvailableOutputBytes = AvailableOutputFrames * sizeof (float); + } +#endif + +#if _OSX_AU_DEBUG_ + std::cerr << "cb1: avail: #IF = " << AvailableInputFrames + << ", #OF = " << AvailableOutputFrames << std::endl; +#endif + ActualOutputFrames = AvailableOutputFrames; + +// convert the data to the correct rate +// on input, ActualOutputFrames is the number of available output frames + + err = AudioConverterFillComplexBuffer (This->d_AudioConverter, + (AudioConverterComplexInputDataProc)(This->ConverterCallback), + inRefCon, + &ActualOutputFrames, + This->d_OutputBuffer, + NULL); + CheckErrorAndThrow (err, "AudioConverterFillComplexBuffer", + "audio_osx_source::AUInputCallback"); + +// on output, ActualOutputFrames is the actual number of output frames + +#if _OSX_AU_DEBUG_ + std::cerr << "cb2: actual: #IF = " << This->d_n_ActualInputFrames + << ", #OF = " << AvailableOutputFrames << std::endl; + if (This->d_n_ActualInputFrames != AvailableInputFrames) + std::cerr << "cb2.1: avail#IF = " << AvailableInputFrames + << ", actual#IF = " << This->d_n_ActualInputFrames << std::endl; +#endif + } + +// add the output frames to the buffers' queue, checking for overflow + + int l_counter = This->d_n_user_channels; + int res = 0; + + while (--l_counter >= 0) { + float* inBuffer = (float*) This->d_OutputBuffer->mBuffers[l_counter].mData; + +#if _OSX_AU_DEBUG_ + std::cerr << "cb3: enqueuing audio data." << std::endl; +#endif + + int l_res = This->d_buffers[l_counter]->enqueue (inBuffer, ActualOutputFrames); + if (l_res == -1) + res = -1; + } + + if (res == -1) { +// data coming in too fast +// drop oldest buffer + fputs ("aO", stderr); + fflush (stderr); +// set the local number of samples available to the max + This->d_queueSampleCount = This->d_buffers[0]->buffer_length_items (); + } else { +// keep up the local sample count + This->d_queueSampleCount += ActualOutputFrames; + } + +#if _OSX_AU_DEBUG_ + std::cerr << "cb4: #OI = " << ActualOutputFrames + << ", #Cnt = " << This->d_queueSampleCount + << ", mSC = " << This->d_max_sample_count << std::endl; +#endif + +// signal that data is available, if appropraite + This->d_cond_data->notify_one (); + +#if _OSX_AU_DEBUG_ + std::cerr << "cb5: returning." << std::endl; +#endif + + return (err); +} + +void +audio_osx_source::SetDefaultInputDeviceAsCurrent +() +{ +// set the default input device + AudioDeviceID deviceID; + UInt32 dataSize = sizeof (AudioDeviceID); + AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice, + &dataSize, + &deviceID); + OSStatus err = AudioUnitSetProperty (d_InputAU, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &deviceID, + sizeof (AudioDeviceID)); + CheckErrorAndThrow (err, "AudioUnitSetProperty Current Device", + "audio_osx_source::SetDefaultInputDeviceAsCurrent"); +} + +#if _OSX_DO_LISTENERS_ +OSStatus +audio_osx_source::HardwareListener +(AudioHardwarePropertyID inPropertyID, + void *inClientData) +{ + OSStatus err = noErr; + audio_osx_source* This = static_cast<audio_osx_source*>(inClientData); + + std::cerr << "a_o_s::HardwareListener" << std::endl; + +// set the new default hardware input device for use by our AU + + This->SetDefaultInputDeviceAsCurrent (); + +// reset the converter to tell it that the stream has changed + + err = AudioConverterReset (This->d_AudioConverter); + CheckErrorAndThrow (err, "AudioConverterReset", + "audio_osx_source::UnitListener"); + + return (err); +} + +OSStatus +audio_osx_source::UnitListener +(void *inRefCon, + AudioUnit ci, + AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement) +{ + OSStatus err = noErr; + audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon); + AudioStreamBasicDescription asbd; + + std::cerr << "a_o_s::UnitListener" << std::endl; + +// get the converter's input ASBD (for printing) + + UInt32 propertySize = sizeof (asbd); + err = AudioConverterGetProperty (This->d_AudioConverter, + kAudioConverterCurrentInputStreamDescription, + &propertySize, + &asbd); + CheckErrorAndThrow (err, "AudioConverterGetProperty " + "CurrentInputStreamDescription", + "audio_osx_source::UnitListener"); + + std::cerr << "UnitListener: Input Source changed." << std::endl + << "Old Source Output Info:" << std::endl; + PrintStreamDesc (&asbd); + +// get the new input unit's output ASBD + + propertySize = sizeof (asbd); + err = AudioUnitGetProperty (This->d_InputAU, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, 1, + &asbd, &propertySize); + CheckErrorAndThrow (err, "AudioUnitGetProperty StreamFormat", + "audio_osx_source::UnitListener"); + + std::cerr << "New Source Output Info:" << std::endl; + PrintStreamDesc (&asbd); + +// set the converter's input ASBD to this + + err = AudioConverterSetProperty (This->d_AudioConverter, + kAudioConverterCurrentInputStreamDescription, + propertySize, + &asbd); + CheckErrorAndThrow (err, "AudioConverterSetProperty " + "CurrentInputStreamDescription", + "audio_osx_source::UnitListener"); + +// reset the converter to tell it that the stream has changed + + err = AudioConverterReset (This->d_AudioConverter); + CheckErrorAndThrow (err, "AudioConverterReset", + "audio_osx_source::UnitListener"); + + return (err); +} +#endif diff --git a/gr-audio/lib/osx/audio_osx_source.h b/gr-audio/lib/osx/audio_osx_source.h new file mode 100644 index 000000000..754f0d928 --- /dev/null +++ b/gr-audio/lib/osx/audio_osx_source.h @@ -0,0 +1,115 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_AUDIO_OSX_SOURCE_H +#define INCLUDED_AUDIO_OSX_SOURCE_H + +#include <gr_audio_source.h> +#include <string> +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> +#include <circular_buffer.h> + +/*! + * \brief audio source using OSX + * + * Input signature is one or two streams of floats. + * Samples must be in the range [-1,1]. + */ + +class audio_osx_source : public gr_sync_block { + + Float64 d_deviceSampleRate, d_outputSampleRate; + int d_channel_config; + UInt32 d_inputBufferSizeFrames, d_inputBufferSizeBytes; + UInt32 d_outputBufferSizeFrames, d_outputBufferSizeBytes; + UInt32 d_deviceBufferSizeFrames, d_deviceBufferSizeBytes; + UInt32 d_leadSizeFrames, d_leadSizeBytes; + UInt32 d_trailSizeFrames, d_trailSizeBytes; + UInt32 d_extraBufferSizeFrames, d_extraBufferSizeBytes; + UInt32 d_queueSampleCount, d_max_sample_count; + UInt32 d_n_AvailableInputFrames, d_n_ActualInputFrames; + UInt32 d_n_user_channels, d_n_max_channels, d_n_deviceChannels; + bool d_do_block, d_passThrough, d_waiting_for_data; + gruel::mutex* d_internal; + gruel::condition_variable* d_cond_data; + circular_buffer<float>** d_buffers; + +// AudioUnits and Such + AudioUnit d_InputAU; + AudioBufferList* d_InputBuffer; + AudioBufferList* d_OutputBuffer; + AudioConverterRef d_AudioConverter; + +public: + audio_osx_source (int sample_rate = 44100, + const std::string device_name = "", + bool do_block = true, + int channel_config = -1, + int max_sample_count = -1); + + ~audio_osx_source (); + + bool start (); + bool stop (); + bool IsRunning (); + + bool check_topology (int ninputs, int noutputs); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +private: + void SetDefaultInputDeviceAsCurrent (); + + void AllocAudioBufferList (AudioBufferList** t_ABL, + UInt32 n_channels, + UInt32 inputBufferSizeBytes); + + void FreeAudioBufferList (AudioBufferList** t_ABL); + + static OSStatus ConverterCallback (AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outASPD, + void* inUserData); + + static OSStatus AUInputCallback (void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); +#if _OSX_DO_LISTENERS_ + static OSStatus UnitListener (void *inRefCon, + AudioUnit ci, + AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement); + + static OSStatus HardwareListener (AudioHardwarePropertyID inPropertyID, + void *inClientData); +#endif +}; + +#endif /* INCLUDED_AUDIO_OSX_SOURCE_H */ diff --git a/gr-audio/lib/windows/audio_windows_sink.cc b/gr-audio/lib/windows/audio_windows_sink.cc new file mode 100644 index 000000000..e3f67a8f4 --- /dev/null +++ b/gr-audio/lib/windows/audio_windows_sink.cc @@ -0,0 +1,323 @@ +/* -*- c++ -*- */ +/* +* Copyright 2004-2011 Free Software Foundation, Inc. +* +* This file is part of GNU Radio +* +* GNU Radio 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 3, or (at your option) +* any later version. +* +* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +* the Free Software Foundation, Inc., 51 Franklin Street, +* Boston, MA 02110-1301, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gr_audio_registry.h" +#include <audio_windows_sink.h> +#include <gr_io_signature.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <iostream> +#include <stdexcept> +#include <string> +#include <sstream> + +AUDIO_REGISTER_SINK(REG_PRIO_HIGH, windows)( + int sampling_rate, const std::string &device_name, bool +){ + return audio_sink::sptr(new audio_windows_sink(sampling_rate, device_name)); +} + +static const double CHUNK_TIME = 0.1; //0.001; // 100 ms + +// FIXME these should query some kind of user preference + +static std::string +default_device_name () +{ + return "WAVE_MAPPER"; +} + +audio_windows_sink::audio_windows_sink (int sampling_freq, const std::string device_name) + : audio_sink ("audio_windows_sink", + gr_make_io_signature (1, 2, sizeof (float)), + gr_make_io_signature (0, 0, 0)), + d_sampling_freq (sampling_freq), + d_device_name (device_name.empty ()? default_device_name () : device_name), + d_fd (-1), d_buffer (0), d_chunk_size (0) +{ + d_wave_write_event = CreateEvent (NULL, FALSE, FALSE, NULL); + if (open_waveout_device () < 0) + { + //fprintf (stderr, "audio_windows_sink:open_waveout_device() failed\n"); + perror ("audio_windows_sink:open_waveout_device( ) failed\n"); + throw + std::runtime_error ("audio_windows_sink:open_waveout_device() failed"); + } + + d_chunk_size = (int) (d_sampling_freq * CHUNK_TIME); + set_output_multiple (d_chunk_size); + + d_buffer = new short[d_chunk_size * 2]; + +} + +audio_windows_sink::~audio_windows_sink () +{ + /* Free the callback Event */ + CloseHandle (d_wave_write_event); + waveOutClose (d_h_waveout); + delete[]d_buffer; +} + +int +audio_windows_sink::work (int noutput_items, + gr_vector_const_void_star & input_items, + gr_vector_void_star & output_items) +{ + const float *f0, *f1; + bool playtestsound = false; + if (playtestsound) + { + // dummy + + f0 = (const float *) input_items[0]; + + for (int i = 0; i < noutput_items; i += d_chunk_size) + { + for (int j = 0; j < d_chunk_size; j++) + { + d_buffer[2 * j + 0] = (short) (sin (2.0 * 3.1415926535897932384626 * (float) j * 1000.0 / (float) d_sampling_freq) * 8192 + 0); //+32767 + d_buffer[2 * j + 1] = d_buffer[2 * j + 0]; + } + f0 += d_chunk_size; + if (write_waveout + ((HPSTR) d_buffer, 2 * d_chunk_size * sizeof (short)) < 0) + { + fprintf (stderr, "audio_windows_sink: write failed\n"); + perror ("audio_windows_sink: write failed"); + } + } + // break; + } + else + { + switch (input_items.size ()) + { + + case 1: // mono input + + f0 = (const float *) input_items[0]; + + for (int i = 0; i < noutput_items; i += d_chunk_size) + { + for (int j = 0; j < d_chunk_size; j++) + { + d_buffer[2 * j + 0] = (short) (f0[j] * 32767); + d_buffer[2 * j + 1] = (short) (f0[j] * 32767); + } + f0 += d_chunk_size; + if (write_waveout + ((HPSTR) d_buffer, 2 * d_chunk_size * sizeof (short)) < 0) + { + //fprintf (stderr, "audio_windows_sink: write failed\n"); + perror ("audio_windows_sink: write failed"); + } + } + break; + + case 2: // stereo input + + f0 = (const float *) input_items[0]; + f1 = (const float *) input_items[1]; + + for (int i = 0; i < noutput_items; i += d_chunk_size) + { + for (int j = 0; j < d_chunk_size; j++) + { + d_buffer[2 * j + 0] = (short) (f0[j] * 32767); + d_buffer[2 * j + 1] = (short) (f1[j] * 32767); + } + f0 += d_chunk_size; + f1 += d_chunk_size; + if (write_waveout + ((HPSTR) d_buffer, 2 * d_chunk_size * sizeof (short)) < 0) + { + //fprintf (stderr, "audio_windows_sink: write failed\n"); + perror ("audio_windows_sink: write failed"); + } + } + break; + } + } + return noutput_items; +} + +int +audio_windows_sink::string_to_int (const std::string & s) +{ + int i; + std::istringstream (s) >> i; + return i; +} //ToInt() + +int +audio_windows_sink::open_waveout_device (void) +{ + + UINT /*UINT_PTR */ u_device_id; + /** Identifier of the waveform-audio output device to open. It can be either a device identifier or a handle of an open waveform-audio input device. You can use the following flag instead of a device identifier. + * + * Value Meaning + * WAVE_MAPPER The function selects a waveform-audio output device capable of playing the given format. + */ + if (d_device_name.empty () || default_device_name () == d_device_name) + u_device_id = WAVE_MAPPER; + else + u_device_id = (UINT) string_to_int (d_device_name); + // Open a waveform device for output using event callback. + + unsigned long result; + //HWAVEOUT outHandle; + WAVEFORMATEX wave_format; + + /* Initialize the WAVEFORMATEX for 16-bit, 44KHz, stereo */ + wave_format.wFormatTag = WAVE_FORMAT_PCM; + wave_format.nChannels = 2; + wave_format.nSamplesPerSec = d_sampling_freq; //44100; + wave_format.wBitsPerSample = 16; + wave_format.nBlockAlign = + wave_format.nChannels * (wave_format.wBitsPerSample / 8); + wave_format.nAvgBytesPerSec = + wave_format.nSamplesPerSec * wave_format.nBlockAlign; + wave_format.cbSize = 0; + + /* Open the (preferred) Digital Audio Out device. */ + result = waveOutOpen (&d_h_waveout, WAVE_MAPPER, &wave_format, (DWORD_PTR) d_wave_write_event, 0, CALLBACK_EVENT | WAVE_ALLOWSYNC); //|WAVE_FORMAT_DIRECT | CALLBACK_EVENT| WAVE_ALLOWSYNC + if (result) + { + fprintf (stderr, + "audio_windows_sink: Failed to open waveform output device.\n"); + perror ("audio_windows_sink: Failed to open waveform output device."); + //LocalUnlock(hFormat); + //LocalFree(hFormat); + //mmioClose(hmmio, 0); + return -1; + } + + // + // Do not Swallow the "open" event. + // + //WaitForSingleObject(d_wave_write_event, INFINITE); + + // Allocate and lock memory for the header. + + d_h_wave_hdr = GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, + (DWORD) sizeof (WAVEHDR)); + if (d_h_wave_hdr == NULL) + { + //GlobalUnlock(hData); + //GlobalFree(hData); + //fprintf (stderr, "audio_windows_sink: Not enough memory for header.\n"); + perror ("audio_windows_sink: Not enough memory for header."); + return -1; + } + + d_lp_wave_hdr = (LPWAVEHDR) GlobalLock (d_h_wave_hdr); + if (d_lp_wave_hdr == NULL) + { + //GlobalUnlock(hData); + //GlobalFree(hData); + //fprintf (stderr, "audio_windows_sink: Failed to lock memory for header.\n"); + perror ("audio_windows_sink: Failed to lock memory for header."); + return -1; + } + //d_lp_wave_hdr->dwFlags = WHDR_DONE; + return 0; +} + +int +audio_windows_sink::write_waveout (HPSTR lp_data, DWORD dw_data_size) +{ + UINT w_result; + int teller = 100; + // After allocation, set up and prepare header. + /*while ((d_lp_wave_hdr->dwFlags & WHDR_DONE)==0 && teller>0) + { + teller--; + Sleep(1); + } */ + // Wait until previous wave write completes (first event is the open event). + WaitForSingleObject (d_wave_write_event, 100); //INFINITE + d_lp_wave_hdr->lpData = lp_data; + d_lp_wave_hdr->dwBufferLength = dw_data_size; + d_lp_wave_hdr->dwFlags = 0L; + /* Clear the WHDR_DONE bit (which the driver set last time that + this WAVEHDR was sent via waveOutWrite and was played). Some + drivers need this to be cleared */ + //d_lp_wave_hdr->dwFlags &= ~WHDR_DONE; + + d_lp_wave_hdr->dwLoops = 0L; + w_result = + waveOutPrepareHeader (d_h_waveout, d_lp_wave_hdr, sizeof (WAVEHDR)); + if (w_result != 0) + { + //GlobalUnlock( hData); + //GlobalFree(hData); + //fprintf (stderr, "audio_windows_sink: Failed to waveOutPrepareHeader. error %i\n",w_result); + perror ("audio_windows_sink: Failed to waveOutPrepareHeader"); + } + // Now the data block can be sent to the output device. The + // waveOutWrite function returns immediately and waveform + // data is sent to the output device in the background. + //while (! readyforplayback) Sleep(1); + //readyforplayback=false; + // + // + + w_result = waveOutWrite (d_h_waveout, d_lp_wave_hdr, sizeof (WAVEHDR)); + if (w_result != 0) + { + //GlobalUnlock( hData); + //GlobalFree(hData); + //fprintf (stderr, "audio_windows_sink: Failed to write block to device.error %i\n",w_result); + perror ("audio_windows_sink: Failed to write block to device"); + switch (w_result) + { + case MMSYSERR_INVALHANDLE: + fprintf (stderr, "Specified device handle is invalid. \n"); + break; + case MMSYSERR_NODRIVER: + fprintf (stderr, " No device driver is present. \n"); + break; + case MMSYSERR_NOMEM: + fprintf (stderr, " Unable to allocate or lock memory. \n"); + break; + case WAVERR_UNPREPARED: + fprintf (stderr, + " The data block pointed to by the pwh parameter hasn't been prepared. \n"); + break; + default: + fprintf (stderr, "Unknown error %i\n", w_result); + } + waveOutUnprepareHeader (d_h_waveout, d_lp_wave_hdr, sizeof (WAVEHDR)); + return -1; + } + // WaitForSingleObject(d_wave_write_event, INFINITE); + return 0; +} diff --git a/gr-audio/lib/windows/audio_windows_sink.h b/gr-audio/lib/windows/audio_windows_sink.h new file mode 100644 index 000000000..6819bd448 --- /dev/null +++ b/gr-audio/lib/windows/audio_windows_sink.h @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_AUDIO_WINDOWS_SINK_H +#define INCLUDED_AUDIO_WINDOWS_SINK_H + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX // stops windef.h defining max/min under cygwin + +#include <windows.h> +#include <mmsystem.h> + +#include <gr_audio_sink.h> +#include <string> + +/*! + * \brief audio sink using winmm mmsystem (win32 only) + * + * input signature is one or two streams of floats. + * Input samples must be in the range [-1,1]. + */ + +class audio_windows_sink : public audio_sink +{ + int d_sampling_freq; + std::string d_device_name; + int d_fd; + short *d_buffer; + int d_chunk_size; + HWAVEOUT d_h_waveout; + HGLOBAL d_h_wave_hdr; + LPWAVEHDR d_lp_wave_hdr; + HANDLE d_wave_write_event; + +protected: + int + string_to_int (const std::string & s); + int + open_waveout_device (void); + int + write_waveout (HPSTR lp_data, DWORD dw_data_size); + +public: + audio_windows_sink (int sampling_freq, const std::string device_name = ""); + ~audio_windows_sink (); + + int + work (int noutput_items, + gr_vector_const_void_star & input_items, + gr_vector_void_star & output_items); +}; + +#endif /* INCLUDED_AUDIO_WINDOWS_SINK_H */ diff --git a/gr-audio/lib/windows/audio_windows_source.cc b/gr-audio/lib/windows/audio_windows_source.cc new file mode 100644 index 000000000..4b657a0e3 --- /dev/null +++ b/gr-audio/lib/windows/audio_windows_source.cc @@ -0,0 +1,205 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gr_audio_registry.h" +#include <audio_windows_source.h> +#include <gr_io_signature.h> +//include <sys/soundcard.h> +//include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <iostream> +#include <stdexcept> + +AUDIO_REGISTER_SOURCE(REG_PRIO_HIGH, windows)( + int sampling_rate, const std::string &device_name, bool +){ + return audio_source::sptr(new audio_windows_source(sampling_rate, device_name)); +} + +static const double CHUNK_TIME = 0.005; // 5 ms + +// FIXME these should query some kind of user preference + +static std::string +default_device_name () +{ + return "/dev/dsp"; +} + +audio_windows_source::audio_windows_source (int sampling_freq, const std::string device_name) + : audio_source ("audio_windows_source", + gr_make_io_signature (0, 0, 0), + gr_make_io_signature (1, 2, sizeof (float))), + d_sampling_freq (sampling_freq), + d_device_name (device_name.empty ()? default_device_name () : device_name), + d_fd (-1), d_buffer (0), d_chunk_size (0) +{ + //FIXME TODO implement me +#if 0 + if ((d_fd = open (d_device_name.c_str (), O_RDONLY)) < 0) + { + fprintf (stderr, "audio_windows_source: "); + perror (d_device_name.c_str ()); + throw + std::runtime_error ("audio_windows_source"); + } + + d_chunk_size = (int) (d_sampling_freq * CHUNK_TIME); + set_output_multiple (d_chunk_size); + + d_buffer = new short[d_chunk_size * 2]; + + int format = AFMT_S16_NE; + int orig_format = format; + if (ioctl (d_fd, SNDCTL_DSP_SETFMT, &format) < 0) + { + std:: + cerr << "audio_windows_source: " << d_device_name << + " ioctl failed\n"; + perror (d_device_name.c_str ()); + throw + std::runtime_error ("audio_windows_source"); + } + + if (format != orig_format) + { + fprintf (stderr, "audio_windows_source: unable to support format %d\n", + orig_format); + fprintf (stderr, " card requested %d instead.\n", format); + } + + // set to stereo no matter what. Some hardware only does stereo + int channels = 2; + if (ioctl (d_fd, SNDCTL_DSP_CHANNELS, &channels) < 0 || channels != 2) + { + perror ("audio_windows_source: could not set STEREO mode"); + throw + std::runtime_error ("audio_windows_source"); + } + + // set sampling freq + int sf = sampling_freq; + if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0) + { + std::cerr << "audio_windows_source: " + << d_device_name << ": invalid sampling_freq " + << sampling_freq << "\n"; + sampling_freq = 8000; + if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0) + { + std:: + cerr << + "audio_windows_source: failed to set sampling_freq to 8000\n"; + throw + std::runtime_error ("audio_windows_source"); + } + } +#endif +} + +audio_windows_source::~audio_windows_source () +{ + /*close (d_fd); + delete [] d_buffer; + */ +} + +int +audio_windows_source::work (int noutput_items, + gr_vector_const_void_star & input_items, + gr_vector_void_star & output_items) +{ + //FIXME TODO implement me +#if 0 + float *f0 = (float *) output_items[0]; + float *f1 = (float *) output_items[1]; // will be invalid if this is mono output + + const int shorts_per_item = 2; // L + R + const int bytes_per_item = shorts_per_item * sizeof (short); + + // To minimize latency, never return more than CHUNK_TIME + // worth of samples per call to work. + // FIXME, we need an API to set this value + + noutput_items = std::min (noutput_items, d_chunk_size); + + int base = 0; + int ntogo = noutput_items; + + while (ntogo > 0) + { + int nbytes = std::min (ntogo, d_chunk_size) * bytes_per_item; + int result_nbytes = read (d_fd, d_buffer, nbytes); + + if (result_nbytes < 0) + { + perror ("audio_windows_source"); + return -1; // say we're done + } + + if ((result_nbytes & (bytes_per_item - 1)) != 0) + { + fprintf (stderr, "audio_windows_source: internal error.\n"); + throw std::runtime_error ("internal error"); + } + + int result_nitems = result_nbytes / bytes_per_item; + + // now unpack samples into output streams + + switch (output_items.size ()) + { + case 1: // mono output + for (int i = 0; i < result_nitems; i++) + { + f0[base + i] = d_buffer[2 * i + 0] * (1.0 / 32767); + } + break; + + case 2: // stereo output + for (int i = 0; i < result_nitems; i++) + { + f0[base + i] = d_buffer[2 * i + 0] * (1.0 / 32767); + f1[base + i] = d_buffer[2 * i + 1] * (1.0 / 32767); + } + break; + + default: + assert (0); + } + + ntogo -= result_nitems; + base += result_nitems; + } + + return noutput_items - ntogo; +#endif + return -1; // EOF +} diff --git a/gr-audio/lib/windows/audio_windows_source.h b/gr-audio/lib/windows/audio_windows_source.h new file mode 100644 index 000000000..36311968d --- /dev/null +++ b/gr-audio/lib/windows/audio_windows_source.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004-2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_AUDIO_WINDOWS_SOURCE_H +#define INCLUDED_AUDIO_WINDOWS_SOURCE_H + +#include <gr_audio_source.h> +#include <string> + +/*! + * \brief audio source using winmm mmsystem (win32 only) + * + * Output signature is one or two streams of floats. + * Output samples will be in the range [-1,1]. + */ + +class audio_windows_source : public audio_source +{ + + int d_sampling_freq; + std::string d_device_name; + int d_fd; + short *d_buffer; + int d_chunk_size; + +public: + audio_windows_source (int sampling_freq, const std::string device_name = ""); + + ~audio_windows_source (); + + int + work (int noutput_items, + gr_vector_const_void_star & input_items, + gr_vector_void_star & output_items); +}; + +#endif /* INCLUDED_AUDIO_WINDOWS_SOURCE_H */ |