1 /* gAlan - Graphical Audio Language
2 * Copyright (C) 1999 Tony Garnock-Jones
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <sys/types.h>
29 #include <sys/ioctl.h>
30 #include <sys/soundcard.h>
36 #include "generator.h"
42 #define GENERATOR_CLASS_NAME "midiseqclock"
43 #define GENERATOR_CLASS_PATH "Misc/MIDI Sequencer Clock"
45 #define MIDI_BUFSIZE 8
48 * Here is the Data....
69 SAMPLETIME miditime_offset
;
70 SAMPLETIME gentime_offset
;
71 SAMPLETIME last_timestamp
;
72 gint midibytestocome
, midibufpos
;
73 unsigned char midibuffer
[MIDI_BUFSIZE
];
74 SAMPLETIME buffer_timestamp
;
75 unsigned char laststatus
;
80 * This is the input callback....
81 * Seems to bo ok for now..
84 PRIVATE
int get_bytes_to_come( unsigned char midistatus
) {
86 switch( midistatus
& 0xf0 ) {
103 PRIVATE
void execute_midi_command( Generator
*g
) {
106 Data
*data
= g
->data
;
107 int channel
= data
->laststatus
& 0x0f;
109 g_print( "Executing MIDI Command %d...\n" , (int) data
->laststatus
);
110 switch( data
->laststatus
& 0xf0 ) {
113 // now the note is in data->midibuffer[0] and the velocity is in [1]
115 //g_print( "lasttimestamp=%d, gen=%d, diff=%d (%fsec)\n", data->last_timestamp, gen_get_sampletime(), data->last_timestamp - gen_get_sampletime(), ((gdouble)(data->last_timestamp - gen_get_sampletime())) / SAMPLE_RATE );
116 gen_init_aevent( &event
, AE_NUMBER
, NULL
, 0, NULL
, 0, data
->last_timestamp
);
118 event
.d
.number
= channel
;
119 gen_send_events(g
, EVT_CHANNEL
, -1, &event
);
121 //g_print( "(%x,%x)\n", data->midibuffer[0], data->midibuffer[1] );
122 event
.d
.number
= data
->midibuffer
[0];
123 gen_send_events(g
, EVT_NOTE
, -1, &event
);
125 event
.d
.number
= data
->midibuffer
[1];
126 gen_send_events(g
, EVT_VELOCITY
, -1, &event
);
133 gen_init_aevent( &event
, AE_NUMBER
, NULL
, 0, NULL
, 0, data
->last_timestamp
);
135 event
.d
.number
= channel
;
136 gen_send_events(g
, EVT_CHANNEL
, -1, &event
);
138 //g_print( "(%x,%x)\n", data->midibuffer[0], data->midibuffer[1] );
139 event
.d
.number
= data
->midibuffer
[0];
140 gen_send_events(g
, EVT_PROGAMCHANGE
, -1, &event
);
148 PRIVATE
void input_callback( Generator
*g
, gint source
, GdkInputCondition condition
) {
150 Data
*data
= g
->data
;
151 unsigned char midievent
[4];
155 l
= read( source
, &midievent
, 4 );
157 for( i
=0; i
<l
; i
+=4 ) {
158 //g_print( "%d midiev: %d, %x\n", i, (int) midievent[i], *((int *) &midievent[i]) & 0xffffff00 );
159 switch( midievent
[i
] ) {
161 //g_print( "TMR_WAIT_ABS: %d\n", *((int *) &midievent[i]) >> 8 );
162 if( data
->miditime_offset
== -1 ) {
163 data
->miditime_offset
= (*((int *) midievent
) >> 8) * SAMPLE_RATE
/ 100;
164 data
->gentime_offset
= gen_get_sampletime();
166 data
->last_timestamp
= (*((int *) &midievent
[i
]) >> 8) * SAMPLE_RATE
/ 100
167 - data
->miditime_offset
+ data
->gentime_offset
;
171 switch( midievent
[1] & 0xf0 ) {
173 switch( midievent
[1] ) {
175 gen_init_aevent(&event
, AE_NUMBER
, NULL
, 0, NULL
, 0, data
->last_timestamp
);
177 gen_send_events(g
, EVT_CLOCK
, -1, &event
);
181 gen_init_aevent(&event
, AE_NUMBER
, NULL
, 0, NULL
, 0, data
->last_timestamp
);
183 gen_send_events(g
, EVT_START
, -1, &event
);
195 data
->laststatus
= midievent
[1];
196 data
->midibytestocome
= get_bytes_to_come( data
->laststatus
);
197 //g_print( "playevent here\n" );
198 data
->midibufpos
= 0;
203 //g_print( "in default... %d ls=%x\n", data->midibytestocome, (int) data->laststatus );
204 if( data
->midibytestocome
) {
206 data
->midibuffer
[data
->midibufpos
++] = midievent
[1];
208 if( (data
->midibytestocome
--) == 1 ) {
210 // Now there is a midicommand in data->midibuffer
211 // status byte is data->laststatus
213 execute_midi_command( g
);
218 // running command.. set bytes_to_come to
219 data
->midibytestocome
= get_bytes_to_come( data
->laststatus
) - 1;
220 data
->midibufpos
= 0;
222 // Store this byte...
223 data
->midibuffer
[data
->midibufpos
++] = midievent
[1];
236 * Constuctor and Destructor
238 * I have to add a global-property to get /dev/input/js0 from config.
239 * Of course make it configurable in plugin-properties.
241 * then i need asserts... But hey it works now :-)
242 * Instance can be added even if configured for js1 and only js0
243 * available (??? confusing having non working component )
247 PRIVATE
int init_instance(Generator
*g
) {
248 Data
*data
= safe_malloc(sizeof(Data
));
251 data
->fd
= open( "/dev/sequencer", O_RDWR
);
256 data
->miditime_offset
= -1;
257 data
->last_timestamp
= gen_get_sampletime();
258 data
->midibytestocome
= 0;
259 data
->laststatus
= 0;
261 // ioctl( data->fd, SNDCTL_SEQ_ACTSENSE_ENABLE, 0 );
262 // ioctl( data->fd, SNDCTL_SEQ_TIMING_ENABLE, 0 );
263 // ioctl( data->fd, SNDCTL_SEQ_RT_ENABLE, 0 );
265 data
->input_tag
= gdk_input_add( data
->fd
, GDK_INPUT_READ
, (GdkInputFunction
) input_callback
, (gpointer
) g
);
269 PRIVATE
void destroy_instance(Generator
*g
) {
270 Data
*data
= g
->data
;
272 gdk_input_remove( data
->input_tag
);
280 * pickle and unpickle
282 * are straight forward....
286 PRIVATE
void unpickle_instance(Generator
*g
, ObjectStoreItem
*item
, ObjectStore
*db
) {
287 Data
*data
= safe_malloc(sizeof(Data
));
290 data
->fd
= open( "/dev/sequencer", O_RDONLY
| O_NONBLOCK
);
291 data
->input_tag
= gdk_input_add( data
->fd
, GDK_INPUT_READ
, (GdkInputFunction
) input_callback
, g
);
293 data
->miditime_offset
= -1;
294 data
->last_timestamp
= gen_get_sampletime();
295 data
->midibytestocome
= 0;
296 data
->laststatus
= 0;
300 PRIVATE
void pickle_instance(Generator
*g
, ObjectStoreItem
*item
, ObjectStore
*db
) {
301 //Data *data = g->data;
305 PRIVATE ControlDescriptor controls
[] = {
306 /* { kind, name, min,max,step,page, size,editable, is_dst,queue_number,
307 init,destroy,refresh,refresh_data }, */
308 { CONTROL_KIND_NONE
, }
317 * check how many buttons and axes we have...
318 * can i change this dynamicly ???
322 PRIVATE
void setup_class(void) {
323 GeneratorClass
*k
= gen_new_generatorclass(GENERATOR_CLASS_NAME
, FALSE
, NUM_EVENT_INPUTS
, NUM_EVENT_OUTPUTS
,
324 NULL
, NULL
, controls
,
325 init_instance
, destroy_instance
,
326 unpickle_instance
, pickle_instance
);
328 gen_configure_event_output(k
, EVT_CLOCK
, "Clock");
329 gen_configure_event_output(k
, EVT_START
, "Start");
330 gen_configure_event_output(k
, EVT_CHANNEL
, "Channel");
331 gen_configure_event_output(k
, EVT_NOTE
, "Note");
332 gen_configure_event_output(k
, EVT_VELOCITY
, "Velocity");
333 gen_configure_event_output(k
, EVT_PROGAMCHANGE
, "Program");
334 gencomp_register_generatorclass(k
, FALSE
, GENERATOR_CLASS_PATH
, NULL
, NULL
);
337 PUBLIC
void init_plugin(void) {