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
27 #include "generator.h"
31 #include "galan_jack.h"
32 #include "galan_lash.h"
34 #include <jack/jack.h>
35 #ifdef HAVE_JACKMIDI_H
36 #include <jack/midiport.h>
40 PRIVATE jack_client_t
*jack_client
= NULL
;
42 PRIVATE jack_port_t
*midi_control_port
= NULL
;
44 PRIVATE AClock
*jack_clock
= NULL
;
46 PRIVATE GStaticMutex transport_clocks_mutex
= G_STATIC_MUTEX_INIT
;
47 PRIVATE GStaticMutex jack_process_callbacks_mutex
= G_STATIC_MUTEX_INIT
;
48 PRIVATE GList
*transport_clocks
= NULL
;
49 PRIVATE GList
*jack_process_callbacks
= NULL
;
51 PRIVATE SAMPLETIME jack_timestamp
= 0;
53 PRIVATE Control
*midi_map
[128*16];
54 PRIVATE Control
*midilearn_target
= NULL
;
55 PRIVATE
int midilearn_CC
= 0;
57 // Registering Process Callbacks (used for midi ports)
59 typedef struct jack_process_callback_t
{
61 jack_process_handler_t handler
;
62 } jack_process_callback_t
;
64 PUBLIC
void galan_jack_register_process_handler( Generator
*g
, jack_process_handler_t handler
) {
65 jack_process_callback_t
*new_cb
= safe_malloc( sizeof( jack_process_callback_t
) );
67 new_cb
->handler
= handler
;
68 jack_process_callbacks
= g_list_append( jack_process_callbacks
, new_cb
);
71 PRIVATE gint
jack_process_callback_cmp(jack_process_callback_t
*a
, jack_process_callback_t
*b
) {
72 return !((a
->g
== b
->g
) && (a
->handler
== b
->handler
));
75 PUBLIC
void galan_jack_deregister_process_handler(Generator
*g
, jack_process_handler_t func
) {
76 jack_process_callback_t jpc
;
79 g_static_mutex_lock( &jack_process_callbacks_mutex
);
82 link
= g_list_find_custom(jack_process_callbacks
, &jpc
, (GCompareFunc
) jack_process_callback_cmp
);
87 jack_process_callbacks
= g_list_remove_link(jack_process_callbacks
, link
);
89 g_static_mutex_unlock( &jack_process_callbacks_mutex
);
93 // Registering of transport callbacks.
95 typedef struct transport_frame_event_t
{
97 transport_frame_event_handler_t handler
;
98 } transport_frame_event_t
;
100 PRIVATE gint
transport_event_handler_cmp(transport_frame_event_t
*a
, transport_frame_event_t
*b
) {
101 return !((a
->g
== b
->g
) && (a
->handler
== b
->handler
));
104 PUBLIC
void galan_jack_register_transport_clock( Generator
*g
, transport_frame_event_handler_t handler
) {
105 transport_frame_event_t
*new_cb
= safe_malloc( sizeof( transport_frame_event_t
) );
107 new_cb
->handler
= handler
;
108 transport_clocks
= g_list_append( transport_clocks
, new_cb
);
111 PUBLIC
void galan_jack_deregister_transport_clock( Generator
*g
, transport_frame_event_handler_t handler
) {
113 transport_frame_event_t jpc
;
116 g_static_mutex_lock( &transport_clocks_mutex
);
118 jpc
.handler
= handler
;
119 link
= g_list_find_custom(transport_clocks
, &jpc
, (GCompareFunc
) transport_event_handler_cmp
);
122 transport_clocks
= g_list_remove_link( transport_clocks
, link
);
126 g_static_mutex_unlock( &transport_clocks_mutex
);
132 PUBLIC
void midilearn_set_target_control( Control
*c
) {
133 midilearn_target
= c
;
136 PUBLIC
void midilearn_remove_control( Control
*c
) {
138 for( i
=0; i
<(128*16); i
++ ) {
139 if( midi_map
[i
] == c
) {
145 PUBLIC
int midilearn_check_result( void ) {
146 if( midilearn_target
!= NULL
)
152 PUBLIC ObjectStoreDatum
*midi_map_pickle(ObjectStore
*db
) {
153 ObjectStoreDatum
*result
= objectstore_datum_new_array(128*16);
156 for (i
= 0; i
< (128*16); i
++) {
158 objectstore_datum_array_set(result
, i
, objectstore_datum_new_object(control_pickle(midi_map
[i
], db
)));
160 objectstore_datum_array_set( result
, i
, NULL
);
166 PUBLIC
void unpickle_midi_map_array(ObjectStoreDatum
*array
, ObjectStore
*db
) {
168 len
= objectstore_datum_array_length(array
);
170 printf( "Error midi_map len wrong !!!" );
174 for (i
= 0; i
< len
; i
++) {
175 ObjectStoreDatum
*elt
= objectstore_datum_array_get(array
, i
);
176 ObjectStoreItem
*item
= objectstore_get_item_by_key(db
, objectstore_datum_object_key(elt
));
178 midi_map
[i
] = control_unpickle(item
);
183 PUBLIC SAMPLETIME
galan_jack_get_timestamp( void ) {
184 return jack_timestamp
;
187 PUBLIC jack_client_t
*galan_jack_get_client(void) {
192 PRIVATE
void process_midi_control_port( jack_nframes_t nframes
) {
195 void *port_buffer
= jack_port_get_buffer( midi_control_port
, nframes
);
196 jack_nframes_t num_jackevents
= jack_midi_get_event_count( port_buffer
);
197 jack_midi_event_t jackevent
;
199 for( i
=0; i
<num_jackevents
; i
++ ) {
200 if( jack_midi_event_get( &jackevent
, port_buffer
, i
) != 0 )
203 if( (jackevent
.buffer
[0] & 0xf0) == 0xb0 ) {
204 int CC
= jackevent
.buffer
[1];
205 int CH
= jackevent
.buffer
[0] & 0x0f;
210 if( midilearn_target
) {
211 midi_map
[CC
+CH
*128] = midilearn_target
;
212 midilearn_target
= NULL
;
213 } else if ( midi_map
[CC
+CH
*128] != NULL
) {
216 Control
*c
= midi_map
[CC
+CH
*128];
218 // XXX: need to lock control here.
219 // and send out the data.
220 gdouble rng
= c
->max
- c
->min
;
221 gdouble cc
= jackevent
.buffer
[2];
222 gdouble val
= c
->min
+ rng
* cc
/127.0;
224 control_emit( c
, val
);
232 PRIVATE
int process_callback( jack_nframes_t nframes
, void *data
) {
234 jack_timestamp
= gen_get_sampletime();
236 process_midi_control_port( nframes
);
238 if( g_static_mutex_trylock( &transport_clocks_mutex
) )
240 if( transport_clocks
) {
241 //jack_transport_info_t trans_info;
242 jack_position_t jack_trans_pos
;
243 jack_transport_state_t jack_trans_state
;
246 jack_trans_state
= jack_transport_query( jack_client
, &jack_trans_pos
);
248 if( jack_trans_state
== JackTransportRolling
) {
250 if( jack_trans_pos
.valid
& JackPositionBBT
) {
251 bpm
= jack_trans_pos
.beats_per_minute
;
256 for( l
= transport_clocks
; l
; l
= g_list_next( l
) ) {
257 transport_frame_event_t
*cb
= l
->data
;
258 cb
->handler( cb
->g
, jack_trans_pos
.frame
, nframes
, bpm
);
262 g_static_mutex_unlock( &transport_clocks_mutex
);
265 if( g_static_mutex_trylock( &jack_process_callbacks_mutex
) )
267 if( jack_process_callbacks
) {
269 for( l
= jack_process_callbacks
; l
; l
= g_list_next( l
) ) {
270 jack_process_callback_t
*cb
= l
->data
;
271 cb
->handler( cb
->g
, nframes
);
274 g_static_mutex_unlock( &jack_process_callbacks_mutex
);
276 gen_clock_mainloop_have_remaining( nframes
);
281 PRIVATE
void jack_shutdown (void *arg
) {
282 g_print( "jack exited :(\n" );
285 PRIVATE
void clock_handler(AClock
*clock
, AClockReason reason
) {
289 jack_deactivate( jack_client
);
293 jack_set_process_callback( jack_client
, (JackProcessCallback
) process_callback
, NULL
);
294 jack_on_shutdown (jack_client
, jack_shutdown
, 0);
296 jack_activate( jack_client
);
299 if( lash_enabled( galan_lash_get_client() ) ) {
300 event
= lash_event_new_with_type(LASH_Jack_Client_Name
);
301 lash_event_set_string(event
, jack_get_client_name( jack_client
) );
302 lash_send_event( galan_lash_get_client(), event
);
307 g_message("Unreachable code reached (jack_output)... reason = %d", reason
);
315 PUBLIC
void init_jack(void) {
318 jack_client
= jack_client_open( "galan", 0, NULL
);
319 if (jack_client
== NULL
) {
320 popup_msgbox("Error", MSGBOX_OK
, 120000, MSGBOX_OK
,
321 "Could not open Jack Client");
324 midi_control_port
= jack_port_register ( jack_client
, "control", JACK_DEFAULT_MIDI_TYPE
, JackPortIsInput
, 0);
325 for(i
=0; i
<(128*16); i
++ ) {
330 PUBLIC
void run_jack(void) {
331 jack_clock
= gen_register_clock(NULL
, "Jack Clock", clock_handler
);
332 gen_select_clock(jack_clock
);
335 PUBLIC
void done_jack(void) {
336 jack_deactivate( jack_client
);
337 jack_client_close( jack_client
);