fluidsynth~.c 12.1 KB
Newer Older
porres's avatar
porres committed
1
/*
porres's avatar
porres committed
2
3
4
5
Copyright: Alexandre Torres Porres, based on the work of
 Frank Barknecht, Jonathan Wilkes and Albert Gräf
 Distributed under the GPLv2+, please see LICENSE below.

porres's avatar
porres committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
LICENSE:
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
porres's avatar
porres committed
21
22
23
24
25

#include "m_pd.h"
#include <fluidsynth.h>
#include <string.h>
#include <unistd.h>
porres's avatar
porres committed
26
27
28

#define MAXSYSEXSIZE 1024 // Size of sysex data list (excluding the F0 [240] and F7 [247] bytes)

porres's avatar
porres committed
29
30
31
static t_class *fluid_tilde_class;
 
typedef struct _fluid_tilde{
porres's avatar
porres committed
32
33
34
35
36
37
38
39
    t_object            x_obj;
    fluid_synth_t      *x_synth;
    fluid_settings_t   *x_settings;
    t_outlet           *x_out_left;
    t_outlet           *x_out_right;
    t_canvas           *x_canvas;
    int                 x_sysex;
    int                 x_ready;
porres's avatar
porres committed
40
    t_atom              x_at[MAXSYSEXSIZE];
porres's avatar
porres committed
41
42
43
    unsigned char       x_type;
    unsigned char       x_data;
    unsigned char       x_channel;
porres's avatar
porres committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
}t_fluid_tilde;

t_int *fluid_tilde_perform(t_int *w){
    t_fluid_tilde *x = (t_fluid_tilde *)(w[1]);
    t_sample *left = (t_sample *)(w[2]);
    t_sample *right = (t_sample *)(w[3]);
    int n = (int)(w[4]);
    fluid_synth_write_float(x->x_synth, n, left, 0, 1, right, 0, 1);
    return(w+5);
}

static void fluid_tilde_dsp(t_fluid_tilde *x, t_signal **sp){
    dsp_add(fluid_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, (t_int)sp[0]->s_n);
}

static void fluid_tilde_free(t_fluid_tilde *x){
    if(x->x_synth)
        delete_fluid_synth(x->x_synth);
    if(x->x_settings)
        delete_fluid_settings(x->x_settings);
}

porres's avatar
porres committed
66
67
static void fluid_info(void){
    post(" - [fluidsynth~] uses fluidsynth version: %s ", FLUIDSYNTH_VERSION);
porres's avatar
porres committed
68
    //            post("[fluidsynth~]: loaded soundfont %s", realname);
porres's avatar
porres committed
69
70
71
    post("\n");
}

porres's avatar
porres committed
72
static void fluid_note(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
73
74
75
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
76
    if(ac == 2 || ac == 3){
porres's avatar
porres committed
77
78
        int key = atom_getintarg(0, ac, av);
        int vel = atom_getintarg(1, ac, av);
porres's avatar
porres committed
79
        int chan = ac > 2 ? atom_getintarg(2, ac, av) : 1;
porres's avatar
porres committed
80
        fluid_synth_noteon(x->x_synth, chan-1, key, vel);
porres's avatar
porres committed
81
82
83
    }
}

porres's avatar
porres committed
84
static void fluid_program_change(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
85
86
87
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
88
89
90
    if(ac == 1 || ac == 2){
        int prog = atom_getintarg(0, ac, av);
        int chan = ac > 1 ? atom_getintarg(1, ac, av) : 1;
porres's avatar
porres committed
91
92
93
94
        fluid_synth_program_change(x->x_synth, chan-1, prog);
    }
}

porres's avatar
porres committed
95
static void fluid_control_change(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
96
97
98
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
99
100
101
102
    if(ac == 2 || ac == 3){
        int ctrl = atom_getintarg(0, ac, av);
        int val = atom_getintarg(1, ac, av);
        int chan = ac > 2 ? atom_getintarg(2, ac, av) : 1;
porres's avatar
porres committed
103
104
105
106
        fluid_synth_cc(x->x_synth, chan-1, ctrl, val);
    }
}

porres's avatar
porres committed
107
static void fluid_pitch_bend(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
108
109
110
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
111
112
113
    if(ac == 1 || ac == 2){
        int val = atom_getintarg(0, ac, av);
        int chan = ac > 1 ? atom_getintarg(1, ac, av) : 1;
porres's avatar
porres committed
114
115
116
117
        fluid_synth_pitch_bend(x->x_synth, chan-1, val);
    }
}

porres's avatar
porres committed
118
static void fluid_bank(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
119
120
121
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
122
123
124
    if(ac == 1 || ac == 2){
        int bank = atom_getintarg(0, ac, av);
        int chan = ac > 1 ? atom_getintarg(1, ac, av) : 1;
porres's avatar
porres committed
125
126
127
128
        fluid_synth_bank_select(x->x_synth, chan-1, bank);
    }
}

porres's avatar
porres committed
129
/*static void fluid_gen(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
130
131
132
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
133
    if(ac == 3){
porres's avatar
porres committed
134
135
        int chan, param;
        float value;
porres's avatar
porres committed
136
137
138
        chan = atom_getintarg(0, ac, av);
        param = atom_getintarg(1, ac, av);
        value = atom_getintarg(2, ac, av);
porres's avatar
porres committed
139
140
        fluid_synth_set_gen(x->x_synth, chan-1, param, value);
    }
porres's avatar
porres committed
141
}*/
porres's avatar
porres committed
142

porres's avatar
porres committed
143
static void fluid_touch(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
144
145
146
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
147
148
149
    if(ac == 1 || ac == 2){
        int val = atom_getintarg(0, ac, av);
        int chan = ac > 1 ? atom_getintarg(1, ac, av) : 1;
porres's avatar
porres committed
150
151
152
153
        fluid_synth_channel_pressure(x->x_synth, chan-1, val);
    }
}

porres's avatar
porres committed
154
static void fluid_polytouch(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
155
156
157
    s = NULL;
    if(x->x_synth == NULL)
        return;
porres's avatar
porres committed
158
159
160
    if(ac == 2 || ac == 3){
        int val = atom_getintarg(0, ac, av);
        int key = atom_getintarg(1, ac, av);
porres's avatar
porres committed
161
        int chan = ac > 2 ? atom_getintarg(2, ac, av) : 1;
porres's avatar
porres committed
162
        fluid_synth_key_pressure(x->x_synth, chan-1, key, val);
porres's avatar
porres committed
163
164
165
    }
}

porres's avatar
porres committed
166
static void fluid_sysex(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
167
    s = NULL;
porres's avatar
porres committed
168
    if(x->x_synth == NULL)
porres's avatar
porres committed
169
        return;
porres's avatar
porres committed
170
    if(ac > 0){
porres's avatar
porres committed
171
172
        char buf[MAXSYSEXSIZE];
        int len = 0;
porres's avatar
porres committed
173
174
        while(len < MAXSYSEXSIZE && len < ac){
            buf[len] = atom_getintarg(len, ac, av);
porres's avatar
porres committed
175
176
177
178
179
180
181
182
183
            len++;
        }
        // TODO: In order to handle bulk dump requests in the future, we will
        // have to pick up fluidsynth's response here and output that to a
        // control outlet (which doesn't exist at present).
        fluid_synth_sysex(x->x_synth, buf, len, NULL, NULL, NULL, 0);
    }
}

porres's avatar
porres committed
184
185
186
static void fluid_float(t_fluid_tilde *x, t_float f){
    if(f >= 0 && f <= 255){
        unsigned char val = (unsigned char)f;
porres's avatar
porres committed
187
        if(val > 127){ // not a data type
porres's avatar
porres committed
188
            x->x_type = val & 0xF0; // get type
porres's avatar
porres committed
189
//            post("x->x_type = %d", (int)x->x_type);
porres's avatar
porres committed
190
191
192
193
194
            x->x_channel = (val & 0x0F) + 1; // get channel
            x->x_ready = (x->x_type == 0xC0 || x->x_type == 0xD0); // ready if program or touch
        }
        else if(x->x_ready){
            switch(x->x_type){
porres's avatar
porres committed
195
                case 0x80: // group 128 (NOTE OFF)
porres's avatar
porres committed
196
197
                    SETFLOAT(&x->x_at[0], (t_float)x->x_data);
                    SETFLOAT(&x->x_at[1], 0); // make it note on with velocity 0
porres's avatar
porres committed
198
199
                    SETFLOAT(&x->x_at[2], (t_float)x->x_channel);
                    fluid_note(x, &s_list, 3, x->x_at);
porres's avatar
porres committed
200
201
202
203
                    break;
                case 0x90: // group 144 (NOTE ON)
                    SETFLOAT(&x->x_at[0], (t_float)x->x_data);
                    SETFLOAT(&x->x_at[1], val);
porres's avatar
porres committed
204
205
                    SETFLOAT(&x->x_at[2], (t_float)x->x_channel);
                    fluid_note(x, &s_list, 3, x->x_at);
porres's avatar
porres committed
206
                    break;
porres's avatar
porres committed
207
                case 0xE0: // pitch bend
porres's avatar
porres committed
208
209
210
                    SETFLOAT(&x->x_at[0], (val << 7) + x->x_data);
                    SETFLOAT(&x->x_at[1], x->x_channel);
                    fluid_pitch_bend(x, &s_list, 2, x->x_at);
porres's avatar
porres committed
211
212
213
214
215
216
217
                    break;
                default:
                    break;
            }
            x->x_type = x->x_ready = 0; // clear
        }
        else{ // not ready, get data and make it ready
porres's avatar
porres committed
218
//        post("x->x_data = %d", (int)x->x_data);
porres's avatar
porres committed
219
220
221
222
223
224
225
226
            x->x_data = val;
            x->x_ready = 1;
        }
    }
    else
        x->x_type = x->x_ready = 0; // clear
}

porres's avatar
porres committed
227
static void fluid_load(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
228
229
    s = NULL;
    if(x->x_synth == NULL){
porres's avatar
porres committed
230
        pd_error(x, "[fluidsynth~]: no fluidsynth");
porres's avatar
porres committed
231
232
        return;
    }
porres's avatar
porres committed
233
234
    if(ac >= 1 && av->a_type == A_SYMBOL){
        const char* filename = atom_getsymbolarg(0, ac, av)->s_name;
porres's avatar
porres committed
235
236
237
238
239
240
241
        const char* ext = strrchr(filename, '.');
        char realdir[MAXPDSTRING], *realname = NULL;
        int fd;
        if(ext && !strchr(ext, '/')){ // extension already supplied, no default extension
            ext = "";
            fd = canvas_open(x->x_canvas, filename, ext, realdir, &realname, MAXPDSTRING, 0);
            if(fd < 0){
porres's avatar
porres committed
242
              pd_error(x, "[fluidsynth~]: can't find soundfont %s", filename);
porres's avatar
porres committed
243
244
245
246
247
248
249
250
251
252
              return;
            }
        }
        else{
            ext = ".sf2"; // let's try sf2
            fd = canvas_open(x->x_canvas, filename, ext, realdir, &realname, MAXPDSTRING, 0);
            if(fd < 0){ // failed
                ext = ".sf3"; // let's try sf3 then
                fd = canvas_open(x->x_canvas, filename, ext, realdir, &realname, MAXPDSTRING, 0);
                if(fd < 0){ // also failed
porres's avatar
porres committed
253
                    pd_error(x, "[fluidsynth~]: can't find soundfont %s", filename);
porres's avatar
porres committed
254
255
256
257
258
259
260
261
                   return;
                }
            }
        }
        // Save the current working directory.
        char buf[MAXPDSTRING], *cwd = getcwd(buf, MAXPDSTRING);
        sys_close(fd);
        chdir(realdir);
porres's avatar
porres committed
262
        if(fluid_synth_sfload(x->x_synth, realname, 0) >= 0)
porres's avatar
porres committed
263
            fluid_synth_program_reset(x->x_synth);
porres's avatar
porres committed
264
        cwd && chdir(cwd); // Restore the working directory.
porres's avatar
porres committed
265
266
267
    }
}

porres's avatar
porres committed
268
/*static void fluid_init(t_fluid_tilde *x, t_symbol *s, int ac, t_atom *av){
porres's avatar
porres committed
269
270
271
272
273
    s = NULL;
    if(x->x_synth)
        delete_fluid_synth(x->x_synth);
    if(x->x_settings)
        delete_fluid_settings(x->x_settings);
porres's avatar
porres committed
274
275
276
277
278
279
280
}*/

static void *fluid_tilde_new(t_symbol *s, int ac, t_atom *av){
    s = NULL;
    t_fluid_tilde *x = (t_fluid_tilde *)pd_new(fluid_tilde_class);
    x->x_synth = NULL;
    x->x_settings = NULL;
porres's avatar
porres committed
281
282
    x->x_sysex = x->x_ready = 0;
    x->x_data = x->x_channel = x->x_type = 0;
porres's avatar
porres committed
283
284
285
    x->x_out_left = outlet_new(&x->x_obj, &s_signal);
    x->x_out_right = outlet_new(&x->x_obj, &s_signal);
    x->x_canvas = canvas_getcurrent();
porres's avatar
porres committed
286
287
    x->x_settings = new_fluid_settings();
    if(x->x_settings == NULL){
porres's avatar
porres committed
288
        pd_error(x, "[fluidsynth~]: couldn't create synth settings\n");
porres's avatar
porres committed
289
        goto end;
porres's avatar
porres committed
290
291
292
293
294
    }
    else{ // load settings:
        fluid_settings_setnum(x->x_settings, "synth.midi-channels", 16);
        fluid_settings_setnum(x->x_settings, "synth.polyphony", 256);
        fluid_settings_setnum(x->x_settings, "synth.gain", 0.600000);
porres's avatar
porres committed
295
        fluid_settings_setnum(x->x_settings, "synth.sample-rate", sys_getsr());
porres's avatar
porres committed
296
297
298
299
300
        fluid_settings_setstr(x->x_settings, "synth.chorus.active", "no");
        fluid_settings_setstr(x->x_settings, "synth.reverb.active", "no");
        fluid_settings_setstr(x->x_settings, "synth.ladspa.active", "no");
        x->x_synth = new_fluid_synth(x->x_settings); // Create fluidsynth instance:
        if(x->x_synth == NULL){
porres's avatar
porres committed
301
            pd_error(x, "[fluidsynth~]: couldn't create synth");
porres's avatar
porres committed
302
            goto end;
porres's avatar
porres committed
303
        }
porres's avatar
porres committed
304
        fluid_load(x, gensym("load"), ac, av); // try to load argument as soundfont
porres's avatar
porres committed
305
    }
porres's avatar
porres committed
306
end:
porres's avatar
porres committed
307
308
309
    return(void *)x;
}
 
porres's avatar
porres committed
310
311
void fluidsynth_tilde_setup(void){
    fluid_tilde_class = class_new(gensym("fluidsynth~"), (t_newmethod)fluid_tilde_new,
porres's avatar
porres committed
312
313
        (t_method)fluid_tilde_free, sizeof(t_fluid_tilde), CLASS_DEFAULT, A_GIMME, 0);
    class_addmethod(fluid_tilde_class, (t_method)fluid_tilde_dsp, gensym("dsp"), A_CANT, 0);
porres's avatar
porres committed
314
    class_addfloat(fluid_tilde_class, (t_method)fluid_float); // raw midi input
porres's avatar
porres committed
315
    class_addmethod(fluid_tilde_class, (t_method)fluid_load, gensym("load"), A_GIMME, 0);
porres's avatar
porres committed
316
317
//    class_addmethod(fluid_tilde_class, (t_method)fluid_gen, gensym("gen"), A_GIMME, 0);
    class_addmethod(fluid_tilde_class, (t_method)fluid_bank, gensym("bank"), A_GIMME, 0);
porres's avatar
porres committed
318
    class_addmethod(fluid_tilde_class, (t_method)fluid_note, gensym("note"), A_GIMME, 0);
porres's avatar
porres committed
319
320
321
    class_addlist(fluid_tilde_class, (t_method)fluid_note); // same as note
    class_addmethod(fluid_tilde_class, (t_method)fluid_control_change, gensym("ctl"), A_GIMME, 0);
    class_addmethod(fluid_tilde_class, (t_method)fluid_program_change, gensym("pgm"), A_GIMME, 0);
porres's avatar
porres committed
322
323
    class_addmethod(fluid_tilde_class, (t_method)fluid_polytouch, gensym("polytouch"), A_GIMME, 0);
    class_addmethod(fluid_tilde_class, (t_method)fluid_touch, gensym("touch"), A_GIMME, 0);
porres's avatar
porres committed
324
    class_addmethod(fluid_tilde_class, (t_method)fluid_pitch_bend, gensym("bend"), A_GIMME, 0);
porres's avatar
porres committed
325
    class_addmethod(fluid_tilde_class, (t_method)fluid_sysex, gensym("sysex"), A_GIMME, 0);
porres's avatar
porres committed
326
    class_addmethod(fluid_tilde_class, (t_method)fluid_info, gensym("info"), 0);
porres's avatar
porres committed
327
}