2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2011 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 static gboolean
master_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
29 struct cbox_master
*m
= ct
->user_data
;
30 if (!strcmp(cmd
->command
, "/status") && !*cmd
->arg_types
)
32 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
34 if (!cbox_execute_on(fb
, NULL
, "/sample_rate", "i", error
, m
->srate
))
38 return cbox_execute_on(fb
, NULL
, "/tempo", "f", error
, m
->tempo
) &&
39 cbox_execute_on(fb
, NULL
, "/timesig", "ii", error
, m
->timesig_nom
, m
->timesig_denom
) &&
40 cbox_execute_on(fb
, NULL
, "/playing", "i", error
, (int)m
->state
) &&
41 cbox_execute_on(fb
, NULL
, "/pos", "i", error
, m
->spb
->song_pos_samples
) &&
42 cbox_execute_on(fb
, NULL
, "/pos_ppqn", "i", error
, m
->spb
->song_pos_ppqn
);
45 if (!strcmp(cmd
->command
, "/tell") && !*cmd
->arg_types
)
47 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
51 return cbox_execute_on(fb
, NULL
, "/playing", "i", error
, (int)m
->state
) &&
52 cbox_execute_on(fb
, NULL
, "/pos", "i", error
, m
->spb
->song_pos_samples
) &&
53 cbox_execute_on(fb
, NULL
, "/pos_ppqn", "i", error
, m
->spb
->song_pos_ppqn
);
56 if (!strcmp(cmd
->command
, "/set_tempo") && !strcmp(cmd
->arg_types
, "f"))
58 cbox_master_set_tempo(m
, CBOX_ARG_F(cmd
, 0));
62 if (!strcmp(cmd
->command
, "/set_timesig") && !strcmp(cmd
->arg_types
, "ii"))
64 cbox_master_set_timesig(m
, CBOX_ARG_I(cmd
, 0), CBOX_ARG_I(cmd
, 1));
68 if (!strcmp(cmd
->command
, "/play") && !strcmp(cmd
->arg_types
, ""))
74 if (!strcmp(cmd
->command
, "/stop") && !strcmp(cmd
->arg_types
, ""))
80 if (!strcmp(cmd
->command
, "/panic") && !strcmp(cmd
->arg_types
, ""))
86 if (!strcmp(cmd
->command
, "/seek_samples") && !strcmp(cmd
->arg_types
, "i"))
88 cbox_master_seek_samples(m
, CBOX_ARG_I(cmd
, 0));
92 if (!strcmp(cmd
->command
, "/seek_ppqn") && !strcmp(cmd
->arg_types
, "i"))
94 cbox_master_seek_ppqn(m
, CBOX_ARG_I(cmd
, 0));
98 if ((!strcmp(cmd
->command
, "/samples_to_ppqn") || !strcmp(cmd
->command
, "/ppqn_to_samples")) &&
99 !strcmp(cmd
->arg_types
, "i"))
101 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
105 if (cmd
->command
[1] == 's')
106 return cbox_execute_on(fb
, NULL
, "/value", "i", error
, cbox_master_samples_to_ppqn(m
, CBOX_ARG_I(cmd
, 0)));
108 return cbox_execute_on(fb
, NULL
, "/value", "i", error
, cbox_master_ppqn_to_samples(m
, CBOX_ARG_I(cmd
, 0)));
112 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Song playback not initialised.");
118 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unknown combination of target path and argument: '%s', '%s'", cmd
->command
, cmd
->arg_types
);
123 static void cbox_master_init(struct cbox_master
*master
, struct cbox_engine
*engine
)
126 master
->tempo
= 120.0;
127 master
->new_tempo
= 120.0;
128 master
->timesig_nom
= 4;
129 master
->timesig_denom
= 4;
130 master
->state
= CMTS_STOP
;
131 master
->engine
= engine
;
134 cbox_command_target_init(&master
->cmd_target
, master_process_cmd
, master
);
137 struct cbox_master
*cbox_master_new(struct cbox_engine
*engine
)
139 struct cbox_master
*master
= malloc(sizeof(struct cbox_master
));
140 cbox_master_init(master
, engine
);
145 void cbox_master_set_sample_rate(struct cbox_master
*master
, int srate
)
147 master
->srate
= srate
;
150 void cbox_master_set_tempo(struct cbox_master
*master
, float tempo
)
152 // XXXKF not realtime-safe; won't crash, but may lose tempo
153 // changes when used multiple times in rapid succession
154 master
->new_tempo
= tempo
;
157 void cbox_master_set_timesig(struct cbox_master
*master
, int beats
, int unit
)
159 master
->timesig_nom
= beats
;
160 master
->timesig_denom
= unit
;
164 void cbox_master_to_bbt(const struct cbox_master *master, struct cbox_bbt *bbt, int time_samples)
166 double second = ((double)time_samples) / master->srate;
167 double beat = master->tempo * second / 60;
168 int beat_int = (int)beat;
169 bbt->bar = beat_int / master->timesig_nom;
170 bbt->beat = beat_int % master->timesig_nom;
171 bbt->tick = (beat - beat_int) * PPQN; // XXXKF what if timesig_denom is not 4?
175 uint32_t cbox_master_song_pos_from_bbt(struct cbox_master *master, const struct cbox_bbt *bbt)
177 double beat = bbt->bar * master->timesig_nom + bbt->beat + bbt->tick * 1.0 / PPQN;
178 return (uint32_t)(master->srate * 60 / master->tempo);
182 #define cbox_master_play_args(ARG)
184 DEFINE_RT_VOID_FUNC(cbox_master
, master
, cbox_master_play
)
186 struct cbox_rt
*rt
= master
->engine
->rt
;
187 if (rt
&& rt
->io
&& rt
->io
->impl
->controltransportfunc
)
189 rt
->io
->impl
->controltransportfunc(rt
->io
->impl
, TRUE
, master
->spb
? master
->spb
->song_pos_samples
: (uint32_t)-1);
190 if (!rt
->io
->impl
->getsynccompletedfunc(rt
->io
->impl
))
191 RT_CALL_AGAIN_LATER();
194 // wait for the notes to be released
195 if (master
->state
== CMTS_STOPPING
)
197 RT_CALL_AGAIN_LATER();
201 master
->state
= CMTS_ROLLING
;
204 #define cbox_master_stop_args(ARG)
206 DEFINE_RT_VOID_FUNC(cbox_master
, master
, cbox_master_stop
)
208 struct cbox_rt
*rt
= master
->engine
->rt
;
209 if (rt
&& rt
->io
&& rt
->io
->impl
->controltransportfunc
)
211 rt
->io
->impl
->controltransportfunc(rt
->io
->impl
, FALSE
, -1);
214 if (master
->state
== CMTS_ROLLING
)
215 master
->state
= CMTS_STOPPING
;
217 if (master
->state
!= CMTS_STOP
)
218 RT_CALL_AGAIN_LATER();
221 struct seek_command_arg
223 struct cbox_master
*master
;
226 gboolean was_rolling
;
227 gboolean status_known
;
228 gboolean seek_in_progress
;
231 static int seek_transport_execute(void *arg_
)
233 struct seek_command_arg
*arg
= arg_
;
235 struct cbox_rt
*rt
= arg
->master
->engine
->rt
;
236 if (rt
&& rt
->io
&& rt
->io
->impl
->controltransportfunc
)
238 if (!arg
->seek_in_progress
)
240 arg
->seek_in_progress
= TRUE
;
241 uint32_t pos
= arg
->target_pos
;
243 arg
->target_pos
= pos
= cbox_master_ppqn_to_samples(arg
->master
, pos
);
245 rt
->io
->impl
->controltransportfunc(rt
->io
->impl
, arg
->master
->state
== CMTS_ROLLING
, pos
);
246 // JACK slow-sync won't be performed if unless transport is rolling
247 if (!arg
->was_rolling
)
249 if (arg
->master
->spb
)
250 cbox_song_playback_seek_samples(arg
->master
->spb
, arg
->target_pos
);
254 if (rt
->io
->impl
->getsynccompletedfunc(rt
->io
->impl
))
256 if (arg
->master
->spb
)
257 cbox_song_playback_seek_samples(arg
->master
->spb
, arg
->target_pos
);
263 // On first pass, check if transport is rolling from the DSP thread
264 if (!arg
->status_known
)
266 arg
->status_known
= TRUE
;
267 arg
->was_rolling
= arg
->master
->state
== CMTS_ROLLING
;
269 // If transport was rolling, stop, release notes, seek, then restart
270 if (arg
->master
->state
== CMTS_ROLLING
)
271 arg
->master
->state
= CMTS_STOPPING
;
273 // wait until transport stopped
274 if (arg
->master
->state
!= CMTS_STOP
)
277 if (arg
->master
->spb
)
280 cbox_song_playback_seek_ppqn(arg
->master
->spb
, arg
->target_pos
, FALSE
);
282 cbox_song_playback_seek_samples(arg
->master
->spb
, arg
->target_pos
);
284 if (arg
->was_rolling
)
285 arg
->master
->state
= CMTS_ROLLING
;
289 void cbox_master_seek_ppqn(struct cbox_master
*master
, uint32_t pos_ppqn
)
291 static struct cbox_rt_cmd_definition cmd
= { NULL
, seek_transport_execute
, NULL
};
292 struct seek_command_arg arg
= { master
, TRUE
, pos_ppqn
, FALSE
, FALSE
, FALSE
};
293 cbox_rt_execute_cmd_sync(master
->engine
->rt
, &cmd
, &arg
);
296 void cbox_master_seek_samples(struct cbox_master
*master
, uint32_t pos_samples
)
298 static struct cbox_rt_cmd_definition cmd
= { NULL
, seek_transport_execute
, NULL
};
299 struct seek_command_arg arg
= { master
, FALSE
, pos_samples
, FALSE
, FALSE
, FALSE
};
300 cbox_rt_execute_cmd_sync(master
->engine
->rt
, &cmd
, &arg
);
303 void cbox_master_panic(struct cbox_master
*master
)
305 cbox_master_stop(master
);
306 struct cbox_midi_buffer buf
;
307 cbox_midi_buffer_init(&buf
);
308 for (int ch
= 0; ch
< 16; ch
++)
310 cbox_midi_buffer_write_inline(&buf
, ch
, 0xB0 + ch
, 120, 0);
311 cbox_midi_buffer_write_inline(&buf
, ch
, 0xB0 + ch
, 123, 0);
312 cbox_midi_buffer_write_inline(&buf
, ch
, 0xB0 + ch
, 121, 0);
314 // Send to all outputs
315 cbox_engine_send_events_to(master
->engine
, NULL
, &buf
);
318 int cbox_master_ppqn_to_samples(struct cbox_master
*master
, int time_ppqn
)
320 double tempo
= master
->tempo
;
324 int idx
= cbox_song_playback_tmi_from_ppqn(master
->spb
, time_ppqn
);
327 const struct cbox_tempo_map_item
*tmi
= &master
->spb
->tempo_map_items
[idx
];
329 time_ppqn
-= tmi
->time_ppqn
;
330 offset
= tmi
->time_samples
;
333 return offset
+ (int)(master
->srate
* 60.0 * time_ppqn
/ (tempo
* PPQN
));
336 int cbox_master_samples_to_ppqn(struct cbox_master
*master
, int time_samples
)
338 double tempo
= master
->tempo
;
342 int idx
= cbox_song_playback_tmi_from_samples(master
->spb
, time_samples
);
343 if (idx
!= -1 && idx
< master
->spb
->tempo_map_item_count
)
345 const struct cbox_tempo_map_item
*tmi
= &master
->spb
->tempo_map_items
[idx
];
347 time_samples
-= tmi
->time_samples
;
348 offset
= tmi
->time_ppqn
;
351 return offset
+ (int)(tempo
* PPQN
* time_samples
/ (master
->srate
* 60.0));
354 void cbox_master_destroy(struct cbox_master
*master
)
358 cbox_song_playback_destroy(master
->spb
);