1387
|
1 /*
|
|
2 *
|
|
3 * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2006
|
|
4 *
|
|
5 * This program is free software; you can redistribute it and/or modify it
|
|
6 * under the terms of the GNU General Public License as published by the
|
|
7 * Free Software Foundation; either version 2 of the License, or (at your
|
|
8 * option) any later version.
|
|
9 *
|
|
10 * This program is distributed in the hope that it will be useful, but
|
|
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13 * General Public License for more details.
|
|
14 *
|
|
15 * You should have received a copy of the GNU General Public License along
|
|
16 * with this program; if not, write to the Free Software Foundation, Inc.,
|
1459
|
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
1387
|
18 *
|
|
19 */
|
|
20
|
|
21 #include "b-fluidsynth.h"
|
|
22 #include "b-fluidsynth-config.h"
|
|
23
|
|
24 /* sequencer instance */
|
|
25 static sequencer_client_t sc;
|
|
26 /* options */
|
|
27 static amidiplug_cfg_fsyn_t amidiplug_cfg_fsyn;
|
|
28
|
|
29
|
|
30 gint backend_info_get( gchar ** name , gchar ** longname , gchar ** desc , gint * ppos )
|
|
31 {
|
|
32 if ( name != NULL )
|
|
33 *name = g_strdup( "fluidsynth" );
|
|
34 if ( longname != NULL )
|
|
35 *longname = g_strdup( "FluidSynth Backend " AMIDIPLUG_VERSION );
|
|
36 if ( desc != NULL )
|
|
37 *desc = g_strdup( _("This backend produces audio by sending MIDI events "
|
|
38 "to FluidSynth, a real-time software synthesizer based "
|
|
39 "on the SoundFont2 specification (www.fluidsynth.org).\n"
|
|
40 "Produced audio can be manipulated via player effect "
|
|
41 "plugins and is processed by chosen ouput plugin.\n"
|
|
42 "Backend written by Giacomo Lozito.") );
|
|
43 if ( ppos != NULL )
|
|
44 *ppos = 2; /* preferred position in backend list */
|
|
45 return 1;
|
|
46 }
|
|
47
|
|
48
|
|
49 gint backend_init( void )
|
|
50 {
|
|
51 /* read configuration options */
|
|
52 i_cfg_read();
|
|
53
|
|
54 sc.soundfont_id = -1;
|
|
55 sc.sample_rate = amidiplug_cfg_fsyn.fsyn_synth_samplerate;
|
|
56 sc.settings = new_fluid_settings();
|
|
57
|
|
58 fluid_settings_setnum( sc.settings , "synth.sample-rate" , amidiplug_cfg_fsyn.fsyn_synth_samplerate );
|
|
59 if ( amidiplug_cfg_fsyn.fsyn_synth_gain != -1 )
|
|
60 fluid_settings_setnum( sc.settings , "synth.gain" , (gdouble)amidiplug_cfg_fsyn.fsyn_synth_gain / 10 );
|
|
61 if ( amidiplug_cfg_fsyn.fsyn_synth_poliphony != -1 )
|
|
62 fluid_settings_setint( sc.settings , "synth.poliphony" , amidiplug_cfg_fsyn.fsyn_synth_poliphony );
|
|
63 if ( amidiplug_cfg_fsyn.fsyn_synth_reverb == 1 )
|
|
64 fluid_settings_setstr( sc.settings , "synth.reverb.active" , "yes" );
|
|
65 else if ( amidiplug_cfg_fsyn.fsyn_synth_reverb == 0 )
|
|
66 fluid_settings_setstr( sc.settings , "synth.reverb.active" , "no" );
|
|
67 if ( amidiplug_cfg_fsyn.fsyn_synth_chorus == 1 )
|
|
68 fluid_settings_setstr( sc.settings , "synth.chorus.active" , "yes" );
|
|
69 else if ( amidiplug_cfg_fsyn.fsyn_synth_chorus == 0 )
|
|
70 fluid_settings_setstr( sc.settings , "synth.chorus.active" , "no" );
|
|
71
|
|
72 sc.synth = new_fluid_synth( sc.settings );
|
|
73
|
|
74 /* soundfont loader, check if we should load soundfont on backend init */
|
|
75 if ( amidiplug_cfg_fsyn.fsyn_soundfont_load == 0 )
|
|
76 i_soundfont_load();
|
|
77
|
|
78 return 1;
|
|
79 }
|
|
80
|
|
81
|
|
82 gint backend_cleanup( void )
|
|
83 {
|
|
84 if ( sc.soundfont_id != -1 )
|
|
85 fluid_synth_sfunload( sc.synth , sc.soundfont_id , 0 );
|
|
86 delete_fluid_synth( sc.synth );
|
|
87 delete_fluid_settings( sc.settings );
|
|
88
|
|
89 i_cfg_free(); /* free configuration options */
|
|
90
|
|
91 return 1;
|
|
92 }
|
|
93
|
|
94
|
|
95 gint sequencer_get_port_count( void )
|
|
96 {
|
|
97 /* always return a single port here */
|
|
98 return 1;
|
|
99 }
|
|
100
|
|
101
|
|
102 gint sequencer_start( gchar * midi_fname )
|
|
103 {
|
|
104 /* soundfont loader, check if we should load soundfont on first midifile play */
|
|
105 if (( amidiplug_cfg_fsyn.fsyn_soundfont_load == 1 ) && (sc.soundfont_id == -1 ))
|
|
106 i_soundfont_load();
|
|
107
|
|
108 return 1; /* success */
|
|
109 }
|
|
110
|
|
111
|
|
112 gint sequencer_stop( void )
|
|
113 {
|
|
114 return 1; /* success */
|
|
115 }
|
|
116
|
|
117
|
|
118 /* activate sequencer client */
|
|
119 gint sequencer_on( void )
|
|
120 {
|
|
121 sc.last_sample_time = 0;
|
|
122 sc.tick_offset = 0;
|
|
123
|
|
124 sc.timer_seq = g_timer_new(); /* create the sequencer timer */
|
|
125 sc.timer_sample = g_timer_new(); /* create the sampler timer */
|
|
126
|
|
127 return 1; /* success */
|
|
128 }
|
|
129
|
|
130
|
|
131 /* shutdown sequencer client */
|
|
132 gint sequencer_off( void )
|
|
133 {
|
|
134 if ( sc.timer_seq != NULL )
|
|
135 {
|
|
136 g_timer_destroy( sc.timer_seq ); /* destroy the sequencer timer */
|
|
137 sc.timer_seq = NULL;
|
|
138 }
|
|
139 if ( sc.timer_sample != NULL )
|
|
140 {
|
|
141 g_timer_destroy( sc.timer_sample ); /* destroy the sampler timer */
|
|
142 sc.timer_sample = NULL;
|
|
143 }
|
|
144 return 1; /* success */
|
|
145 }
|
|
146
|
|
147
|
|
148 /* queue set tempo */
|
|
149 gint sequencer_queue_tempo( gint tempo , gint ppq )
|
|
150 {
|
|
151 sc.ppq = ppq;
|
|
152 /* sc.cur_tick_per_sec = (gdouble)( ppq * 1000000 ) / (gdouble)tempo; */
|
|
153 sc.cur_microsec_per_tick = (gdouble)tempo / (gdouble)ppq;
|
|
154 return 1;
|
|
155 }
|
|
156
|
|
157
|
|
158 gint sequencer_queue_start( void )
|
|
159 {
|
|
160 g_timer_start( sc.timer_seq ); /* reset the sequencer timer */
|
|
161 g_timer_start( sc.timer_sample ); /* reset the sampler timer */
|
|
162 return 1;
|
|
163 }
|
|
164
|
|
165
|
|
166 gint sequencer_event_init( void )
|
|
167 {
|
|
168 /* common settings for all our events */
|
|
169 return 1;
|
|
170 }
|
|
171
|
|
172
|
|
173 gint sequencer_event_noteon( midievent_t * event )
|
|
174 {
|
|
175 i_sleep( event->tick_real );
|
|
176 fluid_synth_noteon( sc.synth ,
|
|
177 event->data.d[0] ,
|
|
178 event->data.d[1] ,
|
|
179 event->data.d[2] );
|
|
180 return 1;
|
|
181 }
|
|
182
|
|
183
|
|
184 gint sequencer_event_noteoff( midievent_t * event )
|
|
185 {
|
|
186 i_sleep( event->tick_real );
|
|
187 fluid_synth_noteoff( sc.synth ,
|
|
188 event->data.d[0] ,
|
|
189 event->data.d[1] );
|
|
190 return 1;
|
|
191 }
|
|
192
|
|
193
|
|
194 gint sequencer_event_keypress( midievent_t * event )
|
|
195 {
|
|
196 /* KEY PRESSURE events are not handled by FluidSynth sequencer? */
|
|
197 DEBUGMSG( "KEYPRESS EVENT with FluidSynth backend (unhandled)\n" );
|
|
198 i_sleep( event->tick_real );
|
|
199 return 1;
|
|
200 }
|
|
201
|
|
202
|
|
203 gint sequencer_event_controller( midievent_t * event )
|
|
204 {
|
|
205 i_sleep( event->tick_real );
|
|
206 fluid_synth_cc( sc.synth ,
|
|
207 event->data.d[0] ,
|
|
208 event->data.d[1] ,
|
|
209 event->data.d[2] );
|
|
210 return 1;
|
|
211 }
|
|
212
|
|
213
|
|
214 gint sequencer_event_pgmchange( midievent_t * event )
|
|
215 {
|
|
216 i_sleep( event->tick_real );
|
|
217 fluid_synth_program_change( sc.synth ,
|
|
218 event->data.d[0] ,
|
|
219 event->data.d[1] );
|
|
220 return 1;
|
|
221 }
|
|
222
|
|
223
|
|
224 gint sequencer_event_chanpress( midievent_t * event )
|
|
225 {
|
|
226 /* CHANNEL PRESSURE events are not handled by FluidSynth sequencer? */
|
|
227 DEBUGMSG( "CHANPRESS EVENT with FluidSynth backend (unhandled)\n" );
|
|
228 i_sleep( event->tick_real );
|
|
229 return 1;
|
|
230 }
|
|
231
|
|
232
|
|
233 gint sequencer_event_pitchbend( midievent_t * event )
|
|
234 {
|
|
235 gint pb_value = (((event->data.d[2]) & 0x7f) << 7) | ((event->data.d[1]) & 0x7f);
|
|
236 i_sleep( event->tick_real );
|
|
237 fluid_synth_pitch_bend( sc.synth ,
|
|
238 event->data.d[0] ,
|
|
239 pb_value );
|
|
240 return 1;
|
|
241 }
|
|
242
|
|
243
|
|
244 gint sequencer_event_sysex( midievent_t * event )
|
|
245 {
|
|
246 DEBUGMSG( "SYSEX EVENT with FluidSynth backend (unhandled)\n" );
|
|
247 i_sleep( event->tick_real );
|
|
248 return 1;
|
|
249 }
|
|
250
|
|
251
|
|
252 gint sequencer_event_tempo( midievent_t * event )
|
|
253 {
|
|
254 i_sleep( event->tick_real );
|
|
255 /* sc.cur_tick_per_sec = (gdouble)( sc.ppq * 1000000 ) / (gdouble)event->data.tempo; */
|
|
256 sc.cur_microsec_per_tick = (gdouble)event->data.tempo / (gdouble)sc.ppq;
|
|
257 g_timer_start( sc.timer_seq ); /* reset the sequencer timer */
|
|
258 sc.tick_offset = event->tick_real;
|
|
259 return 1;
|
|
260 }
|
|
261
|
|
262
|
|
263 gint sequencer_event_other( midievent_t * event )
|
|
264 {
|
|
265 /* unhandled */
|
|
266 i_sleep( event->tick_real );
|
|
267 return 1;
|
|
268 }
|
|
269
|
|
270
|
|
271 gint sequencer_output( gpointer * buffer , gint * len )
|
|
272 {
|
|
273 gdouble current_time = g_timer_elapsed( sc.timer_sample , NULL );
|
|
274 if (( current_time > 0.000500 ) &&
|
|
275 ( (current_time - sc.last_sample_time) * 1000000 >=
|
|
276 ((gdouble)amidiplug_cfg_fsyn.fsyn_buffer_size * 1000000 / sc.sample_rate) ))
|
|
277 {
|
|
278 sc.last_sample_time = g_timer_elapsed( sc.timer_sample , NULL ) - 0.000500;
|
|
279 /* number of samples to get = buffer size + incremental margin */
|
|
280 /* g_print( "buf increment: %i\n" , amidiplug_cfg_fsyn.fsyn_buffer_margin +
|
|
281 ((gint)sc.last_sample_time / amidiplug_cfg_fsyn.fsyn_buffer_increment) ); */
|
|
282 *len = (amidiplug_cfg_fsyn.fsyn_buffer_size + amidiplug_cfg_fsyn.fsyn_buffer_margin +
|
|
283 ((gint)sc.last_sample_time / amidiplug_cfg_fsyn.fsyn_buffer_increment) );
|
|
284 *buffer = g_realloc( *buffer , *len * 4 );
|
|
285 fluid_synth_write_s16( sc.synth , *len , *buffer , 0 , 2 , *buffer , 1 , 2 );
|
|
286 *len = *len * 4; /* return the real buffer size in bytes */
|
|
287 return 1;
|
|
288 }
|
|
289 else
|
|
290 {
|
|
291 G_USLEEP( 500 );
|
|
292 return 0;
|
|
293 }
|
|
294 }
|
|
295
|
|
296
|
|
297 gint sequencer_output_shut( guint max_tick , gint skip_offset )
|
|
298 {
|
|
299 fluid_synth_system_reset( sc.synth ); /* all notes off and channels reset */
|
|
300 return 1;
|
|
301 }
|
|
302
|
|
303
|
|
304 /* unimplemented, for autonomous audio == FALSE volume is set by the
|
|
305 output plugin mixer controls and is not handled by input plugins */
|
|
306 gint audio_volume_get( gint * left_volume , gint * right_volume )
|
|
307 {
|
|
308 return 0;
|
|
309 }
|
|
310 gint audio_volume_set( gint left_volume , gint right_volume )
|
|
311 {
|
|
312 return 0;
|
|
313 }
|
|
314
|
|
315
|
|
316 gint audio_info_get( gint * channels , gint * bitdepth , gint * samplerate )
|
|
317 {
|
|
318 *channels = 2;
|
|
319 *bitdepth = 16; /* always 16 bit, we use fluid_synth_write_s16() */
|
|
320 *samplerate = amidiplug_cfg_fsyn.fsyn_synth_samplerate;
|
|
321 return 1; /* valid information */
|
|
322 }
|
|
323
|
|
324
|
|
325 gboolean audio_check_autonomous( void )
|
|
326 {
|
|
327 return FALSE; /* FluidSynth gives produced audio back to player */
|
|
328 }
|
|
329
|
|
330
|
|
331
|
|
332 /* ******************************************************************
|
|
333 *** INTERNALS ****************************************************
|
|
334 ****************************************************************** */
|
|
335
|
|
336
|
|
337 void i_sleep( guint tick )
|
|
338 {
|
|
339 gdouble elapsed_tick_usecs = (gdouble)(tick - sc.tick_offset) * sc.cur_microsec_per_tick;
|
|
340 gdouble elapsed_seq_usecs = g_timer_elapsed( sc.timer_seq , NULL ) * 1000000;
|
|
341 if ( elapsed_seq_usecs < elapsed_tick_usecs )
|
|
342 {
|
|
343 G_USLEEP( elapsed_tick_usecs - elapsed_seq_usecs );
|
|
344 }
|
|
345 }
|
|
346
|
|
347
|
|
348 void i_soundfont_load( void )
|
|
349 {
|
|
350 if ( strcmp( amidiplug_cfg_fsyn.fsyn_soundfont_file , "" ) )
|
|
351 {
|
|
352 DEBUGMSG( "loading soundfont %s\n" , amidiplug_cfg_fsyn.fsyn_soundfont_file );
|
|
353 sc.soundfont_id = fluid_synth_sfload( sc.synth , amidiplug_cfg_fsyn.fsyn_soundfont_file , 0 );
|
|
354 if ( sc.soundfont_id == -1 )
|
|
355 g_warning( "unable to load SoundFont file %s\n" , amidiplug_cfg_fsyn.fsyn_soundfont_file );
|
|
356 #ifdef DEBUG
|
|
357 else
|
|
358 DEBUGMSG( "soundfont %s successfully loaded\n" , amidiplug_cfg_fsyn.fsyn_soundfont_file );
|
|
359 #endif
|
|
360 }
|
|
361 else
|
|
362 {
|
|
363 g_warning( "FluidSynth backend was selected, but no SoundFont has been specified\n" );
|
|
364 sc.soundfont_id = -1;
|
|
365 }
|
|
366 }
|
|
367
|
|
368
|
|
369 gboolean i_bounds_check( gint value , gint min , gint max )
|
|
370 {
|
|
371 if (( value >= min ) && ( value <= max ))
|
|
372 return TRUE;
|
|
373 else
|
|
374 return FALSE;
|
|
375 }
|
|
376
|
|
377
|
|
378 void i_cfg_read( void )
|
|
379 {
|
|
380 pcfg_t *cfgfile;
|
|
381 gchar * config_pathfilename = g_strjoin( "" , g_get_home_dir() , "/" ,
|
|
382 PLAYER_LOCALRCDIR , "/amidi-plug.conf" , NULL );
|
|
383 cfgfile = i_pcfg_new_from_file( config_pathfilename );
|
|
384
|
|
385 if ( !cfgfile )
|
|
386 {
|
|
387 /* fluidsynth backend defaults */
|
|
388 amidiplug_cfg_fsyn.fsyn_soundfont_file = g_strdup( "" );
|
|
389 amidiplug_cfg_fsyn.fsyn_soundfont_load = 1;
|
|
390 amidiplug_cfg_fsyn.fsyn_synth_samplerate = 44100;
|
|
391 amidiplug_cfg_fsyn.fsyn_synth_gain = -1;
|
|
392 amidiplug_cfg_fsyn.fsyn_synth_poliphony = -1;
|
|
393 amidiplug_cfg_fsyn.fsyn_synth_reverb = -1;
|
|
394 amidiplug_cfg_fsyn.fsyn_synth_chorus = -1;
|
|
395 amidiplug_cfg_fsyn.fsyn_buffer_size = 512;
|
|
396 amidiplug_cfg_fsyn.fsyn_buffer_margin = 10;
|
|
397 amidiplug_cfg_fsyn.fsyn_buffer_increment = 18;
|
|
398 }
|
|
399 else
|
|
400 {
|
|
401 i_pcfg_read_string( cfgfile , "fsyn" , "fsyn_soundfont_file" ,
|
|
402 &amidiplug_cfg_fsyn.fsyn_soundfont_file , "" );
|
|
403
|
|
404 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_soundfont_load" ,
|
|
405 &amidiplug_cfg_fsyn.fsyn_soundfont_load , 1 );
|
|
406
|
|
407 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_synth_samplerate" ,
|
|
408 &amidiplug_cfg_fsyn.fsyn_synth_samplerate , 44100 );
|
|
409 if ( !i_bounds_check( amidiplug_cfg_fsyn.fsyn_synth_samplerate , 22050 , 96000 ) )
|
|
410 amidiplug_cfg_fsyn.fsyn_synth_samplerate = 44100;
|
|
411
|
|
412 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_synth_gain" ,
|
|
413 &amidiplug_cfg_fsyn.fsyn_synth_gain , -1 );
|
|
414 if (( amidiplug_cfg_fsyn.fsyn_synth_gain != -1 ) &&
|
|
415 ( !i_bounds_check( amidiplug_cfg_fsyn.fsyn_synth_gain , 0 , 100 ) ))
|
|
416 amidiplug_cfg_fsyn.fsyn_synth_gain = -1;
|
|
417
|
|
418 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_synth_poliphony" ,
|
|
419 &amidiplug_cfg_fsyn.fsyn_synth_poliphony , -1 );
|
|
420 if (( amidiplug_cfg_fsyn.fsyn_synth_poliphony != -1 ) &&
|
|
421 ( !i_bounds_check( amidiplug_cfg_fsyn.fsyn_synth_poliphony , 0 , 100 ) ))
|
|
422 amidiplug_cfg_fsyn.fsyn_synth_poliphony = -1;
|
|
423
|
|
424 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_synth_reverb" ,
|
|
425 &amidiplug_cfg_fsyn.fsyn_synth_reverb , -1 );
|
|
426
|
|
427 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_synth_chorus" ,
|
|
428 &amidiplug_cfg_fsyn.fsyn_synth_chorus , -1 );
|
|
429
|
|
430 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_buffer_size" ,
|
|
431 &amidiplug_cfg_fsyn.fsyn_buffer_size , 512 );
|
|
432 if ( !i_bounds_check( amidiplug_cfg_fsyn.fsyn_buffer_size , 100 , 99999 ) )
|
|
433 amidiplug_cfg_fsyn.fsyn_buffer_size = 512;
|
|
434
|
|
435 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_buffer_margin" ,
|
|
436 &amidiplug_cfg_fsyn.fsyn_buffer_margin , 15 );
|
|
437 if ( !i_bounds_check( amidiplug_cfg_fsyn.fsyn_buffer_margin , 0 , 100 ) )
|
|
438 amidiplug_cfg_fsyn.fsyn_buffer_margin = 15;
|
|
439
|
|
440 i_pcfg_read_integer( cfgfile , "fsyn" , "fsyn_buffer_increment" ,
|
|
441 &amidiplug_cfg_fsyn.fsyn_buffer_increment , 18 );
|
|
442 if ( !i_bounds_check( amidiplug_cfg_fsyn.fsyn_buffer_increment , 6 , 1000 ) )
|
|
443 amidiplug_cfg_fsyn.fsyn_buffer_increment = 18;
|
|
444
|
|
445 i_pcfg_free( cfgfile );
|
|
446 }
|
|
447
|
|
448 g_free( config_pathfilename );
|
|
449 }
|
|
450
|
|
451
|
|
452 void i_cfg_free( void )
|
|
453 {
|
|
454 g_free( amidiplug_cfg_fsyn.fsyn_soundfont_file );
|
|
455 }
|