SCDoc: Use proper static string constants instead of comparing string literals.
[supercollider.git] / server / supernova / audio_backend / portaudio_backend.hpp
bloba3c85320f07fcc4b2b8eb4ce7118669c30cb440b
1 // portaudio backend
2 // Copyright (C) 2006, 2007, 2008, 2012 Tim Blechmann
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; see the file COPYING. If not, write to
16 // the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 // Boston, MA 02111-1307, USA.
19 #ifndef _PORTAUDIO_HPP
20 #define _PORTAUDIO_HPP
22 #include <cstdio>
24 #include "portaudio.h"
25 #ifdef HAVE_PORTAUDIO_CONFIG_H
26 #include "portaudio/portaudio_config.h"
27 #endif /* HAVE_PORTAUDIO_CONFIG_H */
29 #include "audio_backend_common.hpp"
30 #include "utilities/branch_hints.hpp"
33 namespace nova {
35 /** \brief portaudio backend for supernova
37 * */
38 template <typename engine_functor,
39 typename sample_type = float,
40 bool blocking = false>
41 class portaudio_backend:
42 public detail::audio_delivery_helper<sample_type, float, blocking, false>,
43 public detail::audio_settings_basic,
44 protected engine_functor
46 typedef detail::audio_delivery_helper<sample_type, float, blocking, false> super;
48 public:
49 portaudio_backend(void):
50 stream(NULL), blocksize_(0)
52 int err = Pa_Initialize();
53 report_error(err, true);
56 list_devices();
58 #ifdef PA_HAVE_JACK
59 PaJack_SetClientName("SuperNova");
60 #endif
63 ~portaudio_backend(void)
65 if (audio_is_active())
66 deactivate_audio();
68 close_stream();
70 int err = Pa_Terminate();
71 report_error(err);
74 uint32_t get_audio_blocksize(void) const
76 return blocksize_;
79 private:
80 static void report_error(int err, bool throw_exception = false)
82 if (err < 0) {
83 engine_functor::log_printf_("PortAudio error: %s\n", Pa_GetErrorText( err ));
84 if (throw_exception)
85 throw std::runtime_error("PortAudio error");
89 public:
90 static void list_devices(void)
92 int device_number = Pa_GetDeviceCount();
93 if (device_number < 0)
94 report_error(device_number);
96 printf("Available Audio Devices:\n");
97 for (int i = 0; i != device_number; ++i) {
98 const PaDeviceInfo * device_info = Pa_GetDeviceInfo(i);
99 if (device_info) {
100 printf("%d: %s (%d inputs, %d outputs)\n", i, device_info->name,
101 device_info->maxInputChannels, device_info->maxOutputChannels);
104 printf("\n");
107 static bool match_device (std::string const & device_name, int & r_device_index)
109 int device_number = Pa_GetDeviceCount();
110 if (device_number < 0)
111 report_error(device_number);
113 for (int i = 0; i != device_number; ++i) {
114 const PaDeviceInfo * device_info = Pa_GetDeviceInfo(i);
115 if (std::string(device_info->name) == device_name) {
116 r_device_index = i;
117 return true;
120 return false;
123 bool open_stream(std::string const & input_device, unsigned int inchans,
124 std::string const & output_device, unsigned int outchans,
125 unsigned int samplerate, unsigned int pa_blocksize, unsigned int blocksize)
127 int input_device_index, output_device_index;
128 if (!match_device(input_device, input_device_index) || !match_device(output_device, output_device_index))
129 return false;
131 PaStreamParameters in_parameters, out_parameters;
133 if (inchans) {
134 in_parameters.channelCount = inchans;
135 in_parameters.device = input_device_index;
136 in_parameters.sampleFormat = paFloat32 | paNonInterleaved;
137 in_parameters.suggestedLatency = Pa_GetDeviceInfo(in_parameters.device)->defaultLowInputLatency;
138 in_parameters.hostApiSpecificStreamInfo = NULL;
141 if (outchans) {
142 out_parameters.channelCount = outchans;
143 out_parameters.device = output_device_index;
144 out_parameters.sampleFormat = paFloat32 | paNonInterleaved;
145 out_parameters.suggestedLatency = Pa_GetDeviceInfo(out_parameters.device)->defaultLowOutputLatency;
146 out_parameters.hostApiSpecificStreamInfo = NULL;
149 PaStreamParameters * in_stream_parameters = inchans ? &in_parameters : NULL;
150 PaStreamParameters * out_stream_parameters = outchans ? &out_parameters : NULL;
152 PaError supported = Pa_IsFormatSupported(in_stream_parameters, out_stream_parameters, samplerate);
153 report_error(supported);
154 if (supported != 0)
155 return false;
157 callback_initialized = false;
158 blocksize_ = blocksize;
160 PaError opened = Pa_OpenStream(&stream, in_stream_parameters, out_stream_parameters,
161 samplerate, pa_blocksize, paNoFlag,
162 &portaudio_backend::pa_process, this);
164 report_error(opened);
166 if (opened != paNoError)
167 return false;
169 input_channels = inchans;
170 super::input_samples.resize(inchans);
171 output_channels = outchans;
172 super::output_samples.resize(outchans);
173 samplerate_ = samplerate;
174 return true;
177 void close_stream(void)
179 if (stream == NULL)
180 return;
182 deactivate_audio();
184 int err = Pa_CloseStream(stream);
185 report_error(err);
186 stream = NULL;
189 void activate_audio()
191 assert(stream);
192 int err = Pa_StartStream(stream);
193 report_error(err);
196 bool audio_is_active(void)
198 int is_active = Pa_IsStreamActive(stream);
199 if (is_active == 1)
200 return true;
201 if (is_active == 0)
202 return false;
204 report_error(is_active);
205 return false;
208 void deactivate_audio()
210 if (audio_is_active()) {
211 PaError err = Pa_StopStream(stream);
212 report_error(err);
216 bool audiostream_ready(void)
218 return stream;
221 private:
222 int perform(const void *inputBuffer, void *outputBuffer, unsigned long frames,
223 const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags)
225 if (unlikely(!callback_initialized)) {
226 engine_functor::init_thread();
227 engine_functor::sync_clock();
228 callback_initialized = true;
231 if (statusFlags & (paInputOverflow | paInputUnderflow | paOutputOverflow | paOutputUnderflow))
232 engine_functor::sync_clock();
234 const float * inputs[input_channels];
235 float * const *in = static_cast<float * const *>(inputBuffer);
236 for (uint16_t i = 0; i != input_channels; ++i)
237 inputs[i] = in[i];
239 float * outputs[output_channels];
240 float **out = static_cast<float **>(outputBuffer);
241 for (uint16_t i = 0; i != output_channels; ++i)
242 outputs[i] = out[i];
244 unsigned long processed = 0;
245 while (processed != frames) {
246 super::fetch_inputs(inputs, blocksize_, input_channels);
247 engine_functor::run_tick();
248 super::deliver_outputs(outputs, blocksize_, output_channels);
249 processed += blocksize_;
252 return paContinue;
255 static int pa_process(const void *input, void *output, unsigned long frame_count,
256 const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags status_flags, void * user_data)
258 portaudio_backend * self = static_cast<portaudio_backend*>(user_data);
259 return self->perform(input, output, frame_count, time_info, status_flags);
262 PaStream *stream;
263 uint32_t blocksize_;
264 bool callback_initialized;
267 } /* namespace nova */
269 #endif /* _PORTAUDIO_HPP */