1 /*****************************************************************************
2 * fluidsynth.c: Software MIDI synthesizer using libfluidsynth
3 *****************************************************************************
4 * Copyright © 2007 Rémi Denis-Courmont
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
26 #include <vlc_common.h>
27 #include <vlc_plugin.h>
28 #include <vlc_codec.h>
29 #include <vlc_dialog.h>
32 #if defined( _POSIX_VERSION ) && !defined(__ANDROID__)
36 /* On Win32, we link statically */
38 # define FLUIDSYNTH_NOT_A_DLL
41 #ifndef HAVE_FLUIDLITE_H
42 #include <fluidsynth.h>
44 #include <fluidlite.h>
47 #define SOUNDFONT_TEXT N_("Sound fonts")
48 #define SOUNDFONT_LONGTEXT N_( \
49 "A sound fonts file is required for software synthesis." )
51 #define CHORUS_TEXT N_("Chorus")
53 #define GAIN_TEXT N_("Synthesis gain")
54 #define GAIN_LONGTEXT N_("This gain is applied to synthesis output. " \
55 "High values may cause saturation when many notes are played at a time." )
57 #define POLYPHONY_TEXT N_("Polyphony")
58 #define POLYPHONY_LONGTEXT N_( \
59 "The polyphony defines how many voices can be played at a time. " \
60 "Larger values require more processing power.")
62 #define REVERB_TEXT N_("Reverb")
64 #define SAMPLE_RATE_TEXT N_("Sample rate")
66 static int Open (vlc_object_t
*);
67 static void Close (vlc_object_t
*);
70 set_description (N_("FluidSynth MIDI synthesizer"))
71 set_capability ("audio decoder", 100)
72 set_shortname (N_("FluidSynth"))
73 set_category (CAT_INPUT
)
74 set_subcategory (SUBCAT_INPUT_ACODEC
)
75 set_callbacks (Open
, Close
)
76 add_loadfile ("soundfont", "",
77 SOUNDFONT_TEXT
, SOUNDFONT_LONGTEXT
, false)
78 add_bool ("synth-chorus", true, CHORUS_TEXT
, CHORUS_TEXT
, false)
79 add_float ("synth-gain", .5, GAIN_TEXT
, GAIN_LONGTEXT
, false)
80 change_float_range (0., 10.)
81 add_integer ("synth-polyphony", 256,
82 POLYPHONY_TEXT
, POLYPHONY_LONGTEXT
, false)
83 change_integer_range (1, 65535)
84 add_bool ("synth-reverb", true, REVERB_TEXT
, REVERB_TEXT
, true)
85 add_integer ("synth-sample-rate", 44100,
86 SAMPLE_RATE_TEXT
, SAMPLE_RATE_TEXT
, true)
87 change_integer_range (22050, 96000)
93 fluid_settings_t
*settings
;
100 static int DecodeBlock (decoder_t
*p_dec
, block_t
*p_block
);
101 static void Flush (decoder_t
*);
103 static int Open (vlc_object_t
*p_this
)
105 decoder_t
*p_dec
= (decoder_t
*)p_this
;
107 if (p_dec
->fmt_in
.i_codec
!= VLC_CODEC_MIDI
)
110 decoder_sys_t
*p_sys
= malloc (sizeof (*p_sys
));
111 if (unlikely(p_sys
== NULL
))
114 p_sys
->settings
= new_fluid_settings ();
115 p_sys
->synth
= new_fluid_synth (p_sys
->settings
);
116 p_sys
->soundfont
= -1;
118 char *font_path
= var_InheritString (p_this
, "soundfont");
119 if (font_path
!= NULL
)
121 msg_Dbg (p_this
, "loading sound fonts file %s", font_path
);
122 p_sys
->soundfont
= fluid_synth_sfload (p_sys
->synth
, font_path
, 1);
123 if (p_sys
->soundfont
== -1)
124 msg_Err (p_this
, "cannot load sound fonts file %s", font_path
);
127 #if defined( _POSIX_VERSION ) && !defined(__ANDROID__)
132 glob ("/usr/share/sounds/sf2/*.sf2", GLOB_NOESCAPE
, NULL
, &gl
);
133 for (size_t i
= 0; i
< gl
.gl_pathc
; i
++)
135 const char *path
= gl
.gl_pathv
[i
];
137 msg_Dbg (p_this
, "loading sound fonts file %s", path
);
138 p_sys
->soundfont
= fluid_synth_sfload (p_sys
->synth
, path
, 1);
139 if (p_sys
->soundfont
!= -1)
140 break; /* it worked! */
141 msg_Err (p_this
, "cannot load sound fonts file %s", path
);
147 if (p_sys
->soundfont
== -1)
149 msg_Err (p_this
, "sound font file required for synthesis");
150 vlc_dialog_display_error (p_this
, _("MIDI synthesis not set up"),
151 _("A sound font file (.SF2) is required for MIDI synthesis.\n"
152 "Please install a sound font and configure it "
153 "from the VLC preferences "
154 "(Input / Codecs > Audio codecs > FluidSynth).\n"));
155 delete_fluid_synth (p_sys
->synth
);
156 delete_fluid_settings (p_sys
->settings
);
161 fluid_synth_set_chorus_on (p_sys
->synth
,
162 var_InheritBool (p_this
, "synth-chorus"));
163 fluid_synth_set_gain (p_sys
->synth
,
164 var_InheritFloat (p_this
, "synth-gain"));
165 fluid_synth_set_polyphony (p_sys
->synth
,
166 var_InheritInteger (p_this
, "synth-polyphony"));
167 fluid_synth_set_reverb_on (p_sys
->synth
,
168 var_InheritBool (p_this
, "synth-reverb"));
170 p_dec
->fmt_out
.audio
.i_rate
=
171 var_InheritInteger (p_this
, "synth-sample-rate");;
172 fluid_synth_set_sample_rate (p_sys
->synth
, p_dec
->fmt_out
.audio
.i_rate
);
173 p_dec
->fmt_out
.audio
.i_channels
= 2;
174 p_dec
->fmt_out
.audio
.i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
;
175 p_dec
->fmt_out
.i_codec
= VLC_CODEC_FL32
;
176 p_dec
->fmt_out
.audio
.i_bitspersample
= 32;
177 date_Init (&p_sys
->end_date
, p_dec
->fmt_out
.audio
.i_rate
, 1);
178 date_Set (&p_sys
->end_date
, 0);
180 p_dec
->p_sys
= p_sys
;
181 p_dec
->pf_decode
= DecodeBlock
;
182 p_dec
->pf_flush
= Flush
;
187 static void Close (vlc_object_t
*p_this
)
189 decoder_sys_t
*p_sys
= ((decoder_t
*)p_this
)->p_sys
;
191 fluid_synth_sfunload (p_sys
->synth
, p_sys
->soundfont
, 1);
192 delete_fluid_synth (p_sys
->synth
);
193 delete_fluid_settings (p_sys
->settings
);
197 static void Flush (decoder_t
*p_dec
)
199 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
201 date_Set (&p_sys
->end_date
, VLC_TS_INVALID
);
202 //fluid_synth_system_reset (p_sys->synth);
203 fluid_synth_program_reset (p_sys
->synth
);
204 for (unsigned channel
= 0; channel
< 16; channel
++)
205 for (unsigned note
= 0; note
< 128; note
++)
206 fluid_synth_noteoff (p_sys
->synth
, channel
, note
);
209 static int DecodeBlock (decoder_t
*p_dec
, block_t
*p_block
)
211 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
212 block_t
*p_out
= NULL
;
214 if (p_block
== NULL
) /* No Drain */
215 return VLCDEC_SUCCESS
;
217 if (p_block
->i_flags
& (BLOCK_FLAG_DISCONTINUITY
|BLOCK_FLAG_CORRUPTED
))
220 if (p_block
->i_flags
& BLOCK_FLAG_CORRUPTED
)
222 block_Release(p_block
);
223 return VLCDEC_SUCCESS
;
227 if (p_block
->i_pts
> VLC_TS_INVALID
&& !date_Get (&p_sys
->end_date
))
228 date_Set (&p_sys
->end_date
, p_block
->i_pts
);
230 if (p_block
->i_pts
< date_Get (&p_sys
->end_date
))
232 msg_Warn (p_dec
, "MIDI message in the past?");
236 if (p_block
->i_buffer
< 1)
239 uint8_t event
= p_block
->p_buffer
[0];
240 uint8_t channel
= p_block
->p_buffer
[0] & 0xf;
247 if (p_block
->p_buffer
[p_block
->i_buffer
- 1] != 0xF7)
250 msg_Warn (p_dec
, "fragmented SysEx not implemented");
253 fluid_synth_sysex (p_sys
->synth
, (char *)p_block
->p_buffer
+ 1,
254 p_block
->i_buffer
- 2, NULL
, NULL
, NULL
, 0);
257 fluid_synth_system_reset (p_sys
->synth
);
261 uint8_t p1
= (p_block
->i_buffer
> 1) ? (p_block
->p_buffer
[1] & 0x7f) : 0;
262 uint8_t p2
= (p_block
->i_buffer
> 2) ? (p_block
->p_buffer
[2] & 0x7f) : 0;
264 switch (event
& 0xF0)
267 fluid_synth_noteoff (p_sys
->synth
, channel
, p1
);
270 fluid_synth_noteon (p_sys
->synth
, channel
, p1
, p2
);
272 /*case 0xA0: note aftertouch not implemented */
274 fluid_synth_cc (p_sys
->synth
, channel
, p1
, p2
);
277 fluid_synth_program_change (p_sys
->synth
, channel
, p1
);
280 fluid_synth_channel_pressure (p_sys
->synth
, channel
, p1
);
283 fluid_synth_pitch_bend (p_sys
->synth
, channel
, (p2
<< 7) | p1
);
288 (p_block
->i_pts
- date_Get (&p_sys
->end_date
)) * 441 / 10000;
292 if (decoder_UpdateAudioFormat (p_dec
))
294 p_out
= decoder_NewAudioBuffer (p_dec
, samples
);
298 p_out
->i_pts
= date_Get (&p_sys
->end_date
);
299 p_out
->i_length
= date_Increment (&p_sys
->end_date
, samples
)
301 fluid_synth_write_float (p_sys
->synth
, samples
, p_out
->p_buffer
, 0, 2,
302 p_out
->p_buffer
, 1, 2);
304 block_Release (p_block
);
306 decoder_QueueAudio (p_dec
, p_out
);
307 return VLCDEC_SUCCESS
;