1 #ifdef _HAVE_PORTAUDIO_
2 /*
3 * portaudiobackend.c
4 * PortAudio backend.
5 *
6 * for Denemo, a gtk+ frontend to GNU Lilypond
7 * Copyright (C) 2011 Dominic Sacré
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 */
14 #include <glib/gstdio.h>
15 #include "audio/portaudiobackend.h"
16 #include "audio/portaudioutil.h"
17 #ifdef _HAVE_RUBBERBAND_
18 #include <rubberband/rubberband-c.h>
19 #endif
20 #include "audio/midi.h"
21 #include "audio/fluid.h"
22 #include "audio/audiointerface.h"
23
24 #include <portaudio.h>
25 #include <glib.h>
26 #include <string.h>
27 #include "export/audiofile.h"
28 #include "core/utils.h"
29
30 static PaStream *stream;
31 static unsigned long sample_rate;
32
33 static unsigned long playback_frame = 0;
34
35 static gboolean reset_audio = FALSE;
36
37 static gint ready = FALSE;
38
39 static double slowdown = 1.0; //2.0 = twice as long ie half speed.
40 static gboolean rubberband_active = FALSE;
41
42 #ifdef _HAVE_RUBBERBAND_
43 static RubberBandState rubberband;
rubberband_init(DenemoPrefs * config)44 static gint rubberband_init(DenemoPrefs *config) {
45 rubberband = rubberband_new(sample_rate, 2 /* channels */, RubberBandOptionProcessRealTime | RubberBandOptionStretchPrecise,
46 slowdown, 1.0);
47 //rubberband_set_debug_level(rubberband, 3);
48 return 0;
49 }
set_playback_speed(double speed)50 void set_playback_speed (double speed) {
51 if(rubberband==NULL)
52 rubberband_init(&Denemo.prefs);
53 Denemo.project->movement->end_time /= slowdown;
54 Denemo.project->movement->start_time /= slowdown;
55 if(speed>1.01) {
56 slowdown = speed;
57 rubberband_active = TRUE;
58 }
59 else
60 {
61 slowdown = 1.0;
62 rubberband_active = FALSE;
63 }
64 rubberband_set_time_ratio(rubberband, slowdown);
65 Denemo.project->movement->end_time *= slowdown;
66 Denemo.project->movement->start_time *= slowdown;
67 }
68
get_playback_speed(void)69 gdouble get_playback_speed (void)
70 {
71 return slowdown;
72 }
73 #endif
74
75
76 static double
nframes_to_seconds(unsigned long nframes)77 nframes_to_seconds (unsigned long nframes)
78 {
79 return nframes / (double) sample_rate;
80 }
81
82 static unsigned long
seconds_to_nframes(double seconds)83 seconds_to_nframes (double seconds)
84 {
85 return (unsigned long) (sample_rate * seconds);
86 }
87
88 #define MAX_MESSAGE_LENGTH (255) //Allow single sysex blocks, ie 0xF0, length, data...0xF7 where length is one byte.
89
record_audio(float ** buffers,unsigned long frames_per_buffer)90 static void record_audio(float ** buffers, unsigned long frames_per_buffer){
91 // Recording audio out - only one channel is saved at the moment, so source audio (which is dumped in the second channel) is not recorded.
92 if (Denemo.prefs.maxrecordingtime <= 0)
93 return;
94 static FILE *fp = NULL;
95 if (Denemo.project && Denemo.project->audio_recording)
96 {
97 static guint recorded_frames;
98 if (fp == NULL)
99 {
100 const gchar *filename = recorded_audio_filename ();
101 fp = fopen (filename, "wb");
102 recorded_frames = 0;
103 if (fp == NULL)
104 g_warning ("Could not open denemo-output");
105 else
106 g_info ("Opened output file %s", filename);
107 }
108 if (fp)
109 {
110 if (recorded_frames / 44100 < Denemo.prefs.maxrecordingtime)
111 {
112 fwrite (buffers[0], sizeof (float), frames_per_buffer, fp);
113 recorded_frames += frames_per_buffer;
114 }
115 else
116 { //only warn once, don't spew out warnings...
117 if (recorded_frames < G_MAXINT)
118 {
119 recorded_frames = G_MAXINT;
120 g_warning ("Recording length exceeded preference (%d seconds); use the Change Preferences dialog to alter this", Denemo.prefs.maxrecordingtime);
121 }
122 }
123 }
124 }
125 else
126 {
127 if (fp)
128 {
129 fclose (fp);
130 fp = NULL;
131 g_message ("File closed samples are raw data, Little Endian (? or architecture dependent), mono");
132 }
133 }
134 }
135
136 static int
stream_callback(const void * input_buffer,void * output_buffer,unsigned long frames_per_buffer,const PaStreamCallbackTimeInfo * time_info,PaStreamCallbackFlags status_flags,void * user_data)137 stream_callback (const void *input_buffer, void *output_buffer, unsigned long frames_per_buffer, const PaStreamCallbackTimeInfo * time_info, PaStreamCallbackFlags status_flags, void *user_data)
138 {
139 float **buffers = (float **) output_buffer;
140 #ifdef _HAVE_RUBBERBAND_
141 static gboolean initialized = FALSE;
142 if (!initialized) {
143 rubberband_set_max_process_size(rubberband, frames_per_buffer);
144 initialized = TRUE;
145 }
146 #endif
147
148 size_t i;
149 for (i = 0; i < 2; ++i)
150 {
151 memset (buffers[i], 0, frames_per_buffer * sizeof (float));
152 }
153
154 if (!ready)
155 return paContinue;
156
157 #ifdef _HAVE_FLUIDSYNTH_
158 if (reset_audio)
159 {
160 fluidsynth_all_notes_off ();
161 reset_synth_channels ();
162 reset_audio = FALSE;
163 return paContinue;
164 }
165
166 unsigned char event_data[MAX_MESSAGE_LENGTH]; //needs to be long enough for variable length messages...
167 size_t event_length = MAX_MESSAGE_LENGTH;
168 double event_time;
169
170 double until_time = nframes_to_seconds (playback_frame + frames_per_buffer);
171 #ifdef _HAVE_RUBBERBAND_
172 gint available = rubberband_available(rubberband);
173 if((!rubberband_active) || (available < (gint)frames_per_buffer)) {
174 #endif
175
176 while (read_event_from_queue (AUDIO_BACKEND, event_data, &event_length, &event_time, until_time/slowdown))
177 {//g_debug("%d ", event_data[1] );
178 fluidsynth_feed_midi (event_data, event_length); //in fluid.c note fluidsynth api ues fluid_synth_xxx these naming conventions are a bit too similar
179 }
180
181 fluidsynth_render_audio (frames_per_buffer, buffers[0], buffers[1]); //in fluid.c calls fluid_synth_write_float()
182
183 // Now get any audio to mix - dump it in the left hand channel for now
184 event_length = frames_per_buffer;
185 read_event_from_mixer_queue (AUDIO_BACKEND, (void *) buffers[1], &event_length);
186
187 #ifdef _HAVE_RUBBERBAND_
188 }
189 //if there is stuff available use it and give buffers[] to rubber band to process
190 if(rubberband_active)
191 {
192 if(available < (gint)frames_per_buffer)
193 rubberband_process(rubberband, (const float * const*)buffers, frames_per_buffer, 0);
194 available = rubberband_available(rubberband);
195 if(available >= (gint)frames_per_buffer)
196 {
197 rubberband_retrieve(rubberband, buffers, frames_per_buffer);//re-use buffers[] as they are available...
198 write_samples_to_rubberband_queue (AUDIO_BACKEND, buffers[0], frames_per_buffer);
199 write_samples_to_rubberband_queue (AUDIO_BACKEND, buffers[1], frames_per_buffer);
200 available -= frames_per_buffer;
201 }
202 event_length = frames_per_buffer;
203 read_event_from_rubberband_queue (AUDIO_BACKEND, (unsigned char *) buffers[0], &event_length);
204 event_length = frames_per_buffer;
205 read_event_from_rubberband_queue (AUDIO_BACKEND, (unsigned char *) buffers[1], &event_length);
206 }
207 #endif //_HAVE_RUBBERBAND_
208
209 if (until_time < get_playuntil ())
210 {
211 #endif //_HAVE_FLUIDSYNTH_
212 playback_frame += frames_per_buffer;
213 update_playback_time (TIMEBASE_PRIO_AUDIO, nframes_to_seconds (playback_frame));
214 #ifdef _HAVE_FLUIDSYNTH_
215 }
216 #endif //_HAVE_FLUIDSYNTH_
217
218 // This is probably a bad idea to do heavy work in an audio callback
219 record_audio(buffers, frames_per_buffer);
220 return paContinue;
221 }
222
223 static int
actual_portaudio_initialize(DenemoPrefs * config)224 actual_portaudio_initialize (DenemoPrefs * config)
225 {
226 sample_rate = config->portaudio_sample_rate;
227
228 #ifdef _HAVE_FLUIDSYNTH_
229 g_message ("Initializing Fluidsynth");
230 if (fluidsynth_init (config, sample_rate))
231 {
232 g_warning ("Initializing Fluidsynth FAILED!");
233 return -1;
234 }
235 #endif
236 #ifdef _HAVE_RUBBERBAND_
237 g_message ("Initializing Rubberband");
238 if (rubberband_init (config))
239 {
240 g_warning ("Initializing Rubberband FAILED!");
241 return -1;
242 }
243 #endif
244 g_unlink (recorded_audio_filename ());
245
246 g_message ("Initializing PortAudio backend");
247 g_info("PortAudio version: %s", Pa_GetVersionText());
248
249 PaStreamParameters output_parameters;
250 PaError err;
251
252 err = Pa_Initialize ();
253 if (err != paNoError)
254 {
255 g_warning ("Initializing PortAudio failed");
256 return -1;
257 }
258
259 output_parameters.device = get_portaudio_device_index (config->portaudio_device->str);
260
261 if (output_parameters.device == paNoDevice)
262 {
263 output_parameters.device = get_portaudio_device_index ("default");
264 if (output_parameters.device == paNoDevice)
265 {
266 g_warning("No PortAudio device %s and no default either.", config->portaudio_device->str);
267 return -1;
268 }
269 }
270
271 PaDeviceInfo const *info = Pa_GetDeviceInfo (output_parameters.device);
272
273 if (!info)
274 {
275 g_warning ("Invalid device '%s'", config->portaudio_device->str);
276 return -1;
277 }
278
279 char const *api_name = Pa_GetHostApiInfo (info->hostApi)->name;
280 g_message ("Opening output device '%s: %s'", api_name, info->name);
281
282 output_parameters.channelCount = 2;
283 output_parameters.sampleFormat = paFloat32 | paNonInterleaved;
284 output_parameters.suggestedLatency = Pa_GetDeviceInfo (output_parameters.device)->defaultLowOutputLatency;
285 output_parameters.hostApiSpecificStreamInfo = NULL;
286 err = Pa_OpenStream (&stream, NULL, &output_parameters, config->portaudio_sample_rate, config->portaudio_period_size, paNoFlag /* make this a pref??? paClipOff */ , stream_callback, NULL);
287 if (err != paNoError)
288 {
289 g_warning ("Couldn't open output stream");
290 return -1;
291 }
292 err = Pa_StartStream (stream);
293 if (err != paNoError)
294 {
295 g_warning ("Couldn't start output stream");
296 return -1;
297 }
298
299 return 0;
300 }
301
302 static int
ready_now()303 ready_now ()
304 {
305 ready = TRUE;
306 return FALSE;
307 }
308
309 static int
portaudio_initialize(DenemoPrefs * config)310 portaudio_initialize (DenemoPrefs * config)
311 {
312 g_idle_add ((GSourceFunc) ready_now, NULL);
313 return actual_portaudio_initialize (config);
314 }
315
316 static int
portaudio_destroy()317 portaudio_destroy ()
318 {
319 g_message ("Destroying PortAudio backend");
320 ready = FALSE;
321 PaError err;
322
323 err = Pa_CloseStream (stream);
324 if (err != paNoError)
325 {
326 g_warning ("Closing stream failed: %d, %s", err, Pa_GetErrorText (err));
327 return -1;
328 }
329
330 Pa_Terminate ();
331
332 #ifdef _HAVE_FLUIDSYNTH_
333 fluidsynth_shutdown ();
334 #endif
335
336 return 0;
337 }
338
339
340 static int
portaudio_reconfigure(DenemoPrefs * config)341 portaudio_reconfigure (DenemoPrefs * config)
342 {
343 portaudio_destroy ();
344 return portaudio_initialize (config);
345 }
346
347
348 static int
portaudio_start_playing()349 portaudio_start_playing ()
350 {
351 playback_frame = seconds_to_nframes (get_playback_time ());
352 return 0;
353 }
354
355
356 static int
portaudio_stop_playing()357 portaudio_stop_playing ()
358 {
359 reset_audio = TRUE;
360 return 0;
361 }
362
363
364 static int
portaudio_panic()365 portaudio_panic ()
366 {
367 reset_audio = TRUE;
368 return 0;
369 }
370
371
372 backend_t portaudio_backend = {
373 portaudio_initialize,
374 portaudio_destroy,
375 portaudio_reconfigure,
376 portaudio_start_playing,
377 portaudio_stop_playing,
378 portaudio_panic,
379 };
380 #endif //_HAVE_PORTAUDIO_
381