1 #ifndef __SDR_PORTAUDIO_HH__ 2 #define __SDR_PORTAUDIO_HH__ 3 4 #include <portaudio.h> 5 6 #include "buffer.hh" 7 #include "node.hh" 8 #include "config.hh" 9 #include "logger.hh" 10 11 namespace sdr { 12 13 /** "Namespace" to collect all static, PortAudio related functions. */ 14 class PortAudio { 15 public: 16 /** Initializes the PortAudio system, must be called first. */ 17 static void init(); 18 /** Shutdown. */ 19 static void terminate(); 20 21 /** Returns the number of devices available. */ 22 static int numDevices(); 23 /** Returns the index of the default input device. */ 24 static int defaultInputDevice(); 25 /** Returns the index of the default output device. */ 26 static int defaultOutputDevice(); 27 /** Returns @c true of the given device provides an input chanel. */ 28 static bool hasInputStream(int idx); 29 /** Returns @c true of the given device provides an output chanel. */ 30 static bool hasOutputStream(int idx); 31 /** Returns the device name. */ 32 static std::string deviceName(int idx); 33 }; 34 35 36 /** PortAudio playback node. */ 37 class PortSink: public SinkBase 38 { 39 public: 40 /** Constructor. */ 41 PortSink(); 42 /** Destructor. */ 43 virtual ~PortSink(); 44 45 /** Configures the PortAudio output. */ 46 virtual void config(const Config &src_cfg); 47 /** Playback. */ 48 virtual void handleBuffer(const RawBuffer &buffer, bool allow_overwrite); 49 50 protected: 51 /** The PortAudio stream. */ 52 PaStream *_stream; 53 /** The frame-size. */ 54 size_t _frame_size; 55 }; 56 57 58 /** PortAudio input stream as a @c Source. */ 59 template <class Scalar> 60 class PortSource: public Source 61 { 62 public: 63 /** Constructor. */ PortSource(double sampleRate,size_t bufferSize,int dev=-1)64 PortSource(double sampleRate, size_t bufferSize, int dev=-1): 65 Source(), _streamIsOpen(false), _stream(0), _sampleRate(sampleRate), _is_real(true) 66 { 67 // Allocate buffer 68 _buffer = Buffer<Scalar>(bufferSize); 69 _initializeStream(dev); 70 } 71 72 /** Destructor. */ ~PortSource()73 virtual ~PortSource() { if (0 != _stream) { Pa_CloseStream(_stream); } } 74 75 /** Reads (blocking) the next buffer from the PortAudio stream. This function can be 76 * connected to the idle event of the @c Queue. */ next()77 void next() { 78 /// @todo Signal loss of samples in debug mode. 79 /// @bug Drop data if output buffer is in use. 80 Pa_ReadStream(_stream, _buffer.ptr(), _buffer.size()); 81 this->send(_buffer); 82 } 83 84 /** Returns the currently selected input device. */ deviceIndex() const85 int deviceIndex() const { return _deviceIndex; } 86 87 /** Selects the input device, throws a @c ConfigError exception if the device can not 88 * be setup as an input device. Do not call this function, while the @c Queue is running. */ setDeviceIndex(int idx=-1)89 void setDeviceIndex(int idx=-1) { 90 _initializeStream(idx); 91 } 92 93 /** Checks if the given sample rate is supported by the device. */ hasSampleRate(double sampleRate)94 bool hasSampleRate(double sampleRate) { 95 PaStreamParameters params; 96 params.device = _deviceIndex; 97 params.channelCount = _is_real ? 1 : 2; 98 params.sampleFormat = _fmt; 99 params.hostApiSpecificStreamInfo = 0; 100 return paFormatIsSupported == Pa_IsFormatSupported(¶ms, 0, sampleRate); 101 } 102 103 /** Resets the sample rate of the input device. Do not call this function, while the 104 * @c Queue is running. */ setSampleRate(double sampleRate)105 void setSampleRate(double sampleRate) { 106 _sampleRate = sampleRate; 107 _initializeStream(_deviceIndex); 108 } 109 110 protected: 111 /** Device setup. */ _initializeStream(int idx=-1)112 void _initializeStream(int idx=-1) { 113 // Stop and close stream if running 114 if (_streamIsOpen) { 115 Pa_StopStream(_stream); Pa_CloseStream(_stream); _streamIsOpen = false; 116 } 117 118 // Determine input stream format by scalar type 119 switch (Config::typeId<Scalar>()) { 120 case Config::Type_f32: 121 _fmt = paFloat32; 122 _is_real = true; 123 break; 124 case Config::Type_u16: 125 case Config::Type_s16: 126 _fmt = paInt16; 127 _is_real = true; 128 break; 129 case Config::Type_cf32: 130 _fmt = paFloat32; 131 _is_real = false; 132 break; 133 case Config::Type_cu16: 134 case Config::Type_cs16: 135 _fmt = paInt16; 136 _is_real = false; 137 break; 138 default: 139 ConfigError err; 140 err << "Can not configure PortAudio sink: Unsupported format " << Config::typeId<Scalar>() 141 << " must be one of " << Config::Type_u16 << ", " << Config::Type_s16 142 << ", " << Config::Type_f32 << ", " << Config::Type_cu16 << ", " << Config::Type_cs16 143 << " or " << Config::Type_cf32 << "."; 144 throw err; 145 } 146 147 // Assemble config: 148 int numCh = _is_real ? 1 : 2; 149 _deviceIndex = idx < 0 ? Pa_GetDefaultInputDevice() : idx; 150 PaStreamParameters params; 151 params.device = _deviceIndex; 152 params.channelCount = numCh; 153 params.sampleFormat = _fmt; 154 params.suggestedLatency = Pa_GetDeviceInfo(_deviceIndex)->defaultHighInputLatency; 155 params.hostApiSpecificStreamInfo = 0; 156 157 // open stream 158 PaError err = Pa_OpenStream(&_stream, ¶ms, 0, _sampleRate, _buffer.size(), 0, 0, 0); 159 160 // Check for errors 161 if (0 > err) { 162 ConfigError err; 163 err << "Can not open PortAudio input stream!"; throw err; 164 } 165 // Mark stream as open 166 _streamIsOpen = true; 167 // Start stream 168 Pa_StartStream(_stream); 169 170 LogMessage msg(LOG_DEBUG); 171 msg << "Configure PortAudio source: " << std::endl 172 << " sample rate " << _sampleRate << std::endl 173 << " buffer size " << _buffer.size() << std::endl 174 << " format " << Config::typeId<Scalar>() << std::endl 175 << " num chanels " << numCh; 176 Logger::get().log(msg); 177 178 // Set config 179 this->setConfig(Config(Config::typeId<Scalar>(), _sampleRate, _buffer.size(), 1)); 180 } 181 182 183 protected: 184 /** If true, the PortAudio stream, @c _stream is open. */ 185 bool _streamIsOpen; 186 /** The PortAudio input stream. */ 187 PaStream *_stream; 188 /** The current format of the PortAudio stream. */ 189 PaSampleFormat _fmt; 190 /** The current sample rate. */ 191 double _sampleRate; 192 /** The current input device index. */ 193 int _deviceIndex; 194 /** If @c true, the input stream is real (1 chanel), otherwise complex (2 chanels). */ 195 bool _is_real; 196 /** The output buffer. */ 197 Buffer<Scalar> _buffer; 198 }; 199 200 } 201 202 #endif // __SDR_PORTAUDIO_HH__ 203