Mercurial > audlegacy-plugins
comparison src/amidi-plug/amidi-plug.c @ 1414:5f892afeb8e1 audacious-plugins-1.4.0-dr2
- amidi-plug 0.8 beta1; support for gthread and v3 plugin system; needs testing and refinements
author | Giacomo Lozito <james@develia.org> |
---|---|
date | Mon, 06 Aug 2007 14:01:24 +0200 |
parents | 761e17b23e0c |
children | e11d83ce8d16 |
comparison
equal
deleted
inserted
replaced
1413:4731d28ea19d | 1414:5f892afeb8e1 |
---|---|
1 /* | 1 /* |
2 * | 2 * |
3 * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2006 | 3 * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007 |
4 * | 4 * |
5 * This program is free software; you can redistribute it and/or modify it | 5 * This program is free software; you can redistribute it and/or modify it |
6 * under the terms of the GNU General Public License as published by the | 6 * under the terms of the GNU General Public License as published by the |
7 * Free Software Foundation; either version 2 of the License, or (at your | 7 * Free Software Foundation; either version 2 of the License, or (at your |
8 * option) any later version. | 8 * option) any later version. |
86 | 86 |
87 | 87 |
88 static void amidiplug_init( void ) | 88 static void amidiplug_init( void ) |
89 { | 89 { |
90 g_log_set_handler(NULL , G_LOG_LEVEL_WARNING , g_log_default_handler , NULL); | 90 g_log_set_handler(NULL , G_LOG_LEVEL_WARNING , g_log_default_handler , NULL); |
91 | |
92 amidiplug_gettime_mutex = g_mutex_new(); | |
93 amidiplug_playing_mutex = g_mutex_new(); | |
94 amidiplug_pause_cond = g_cond_new(); | |
95 amidiplug_seekonpause_cond = g_cond_new(); | |
96 | |
91 DEBUGMSG( "init, read configuration\n" ); | 97 DEBUGMSG( "init, read configuration\n" ); |
92 /* read configuration for amidi-plug */ | 98 /* read configuration for amidi-plug */ |
93 i_configure_cfg_ap_read(); | 99 i_configure_cfg_ap_read(); |
94 amidiplug_playing_status = AMIDIPLUG_STOP; | 100 amidiplug_playing_status = AMIDIPLUG_STOP; |
95 backend.gmodule = NULL; | 101 backend.gmodule = NULL; |
99 | 105 |
100 | 106 |
101 static void amidiplug_cleanup( void ) | 107 static void amidiplug_cleanup( void ) |
102 { | 108 { |
103 i_backend_unload(); /* unload currently loaded backend */ | 109 i_backend_unload(); /* unload currently loaded backend */ |
110 | |
111 g_mutex_free(amidiplug_gettime_mutex); | |
112 g_mutex_free(amidiplug_playing_mutex); | |
113 g_cond_free(amidiplug_pause_cond); | |
114 g_cond_free(amidiplug_seekonpause_cond); | |
115 amidiplug_pause_cond = NULL; | |
116 amidiplug_seekonpause_cond = NULL; | |
104 } | 117 } |
105 | 118 |
106 | 119 |
107 static void amidiplug_configure( void ) | 120 static void amidiplug_configure( void ) |
108 { | 121 { |
124 } | 137 } |
125 | 138 |
126 | 139 |
127 static void amidiplug_stop( InputPlayback * playback ) | 140 static void amidiplug_stop( InputPlayback * playback ) |
128 { | 141 { |
142 gboolean is_error = FALSE; | |
129 DEBUGMSG( "STOP request at tick: %i\n" , midifile.playing_tick ); | 143 DEBUGMSG( "STOP request at tick: %i\n" , midifile.playing_tick ); |
130 pthread_mutex_lock( &amidiplug_playing_mutex ); | 144 |
131 if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) || | 145 g_mutex_lock( amidiplug_playing_mutex ); |
132 ( amidiplug_playing_status == AMIDIPLUG_STOP )) | 146 if ( amidiplug_playing_status == AMIDIPLUG_ERR ) |
133 { | 147 is_error = TRUE; |
134 amidiplug_playing_status = AMIDIPLUG_STOP; | 148 g_mutex_unlock( amidiplug_playing_mutex ); |
135 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 149 |
136 pthread_join( amidiplug_play_thread , NULL ); | 150 if ( !is_error ) |
137 if ( backend.autonomous_audio == FALSE ) | 151 { |
138 pthread_join( amidiplug_audio_thread , NULL ); | 152 /* if stop wasn't called due to error condition, |
139 DEBUGMSG( "STOP activated (play thread joined)\n" ); | 153 wait until the play thread reach a 'safe state' |
140 } | 154 (that is, when amidiplug_play_thread exists) */ |
141 else if ( amidiplug_playing_status == AMIDIPLUG_PAUSE ) | 155 while ( !amidiplug_play_thread ) |
142 { | 156 g_usleep( 20000 ); |
143 amidiplug_playing_status = AMIDIPLUG_STOP; | 157 } |
144 DEBUGMSG( "STOP activated (from PAUSE to STOP)\n" ); | 158 |
145 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 159 g_mutex_lock( amidiplug_playing_mutex ); |
146 } | 160 amidiplug_playing_status = AMIDIPLUG_STOP; |
147 else /* AMIDIPLUG_ERR */ | 161 g_cond_signal( amidiplug_pause_cond ); |
148 { | 162 g_mutex_unlock( amidiplug_playing_mutex ); |
149 DEBUGMSG( "STOP activated (in error handling, ok)\n" ); | 163 if ( amidiplug_play_thread ) |
150 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 164 { |
151 } | 165 g_thread_join( amidiplug_play_thread ); |
166 amidiplug_play_thread = NULL; | |
167 } | |
168 if (( backend.autonomous_audio == FALSE ) && ( amidiplug_audio_thread )) | |
169 { | |
170 g_thread_join( amidiplug_audio_thread ); | |
171 amidiplug_audio_thread = NULL; | |
172 } | |
173 DEBUGMSG( "STOP activated (play thread joined)\n" ); | |
152 | 174 |
153 /* kill the sequencer (while it may have been already killed if coming | 175 /* kill the sequencer (while it may have been already killed if coming |
154 from pause, it's safe to do anyway since it checks for multiple calls) */ | 176 from pause, it's safe to do anyway since it checks for multiple calls) */ |
155 if ( backend.gmodule != NULL ) | 177 if ( backend.gmodule != NULL ) |
156 backend.seq_off(); | 178 backend.seq_off(); |
174 | 196 |
175 static void amidiplug_pause( InputPlayback * playback, gshort paused ) | 197 static void amidiplug_pause( InputPlayback * playback, gshort paused ) |
176 { | 198 { |
177 if ( paused ) | 199 if ( paused ) |
178 { | 200 { |
201 g_mutex_lock( amidiplug_playing_mutex ); | |
202 if ( amidiplug_playing_status == AMIDIPLUG_SEEK ) | |
203 { | |
204 DEBUGMSG( "handle SEEK ON PAUSE situation\n" , midifile.playing_tick ); | |
205 /* uh oh, we have a pending seek; this can happen when a seek | |
206 is requested while playback is paused; wait for that seek to | |
207 be completed before pausing the song again */ | |
208 while ( amidiplug_playing_status != AMIDIPLUG_PLAY ) | |
209 g_cond_wait( amidiplug_seekonpause_cond , amidiplug_playing_mutex ); | |
210 } | |
211 g_mutex_unlock( amidiplug_playing_mutex ); | |
179 DEBUGMSG( "PAUSE request at tick: %i\n" , midifile.playing_tick ); | 212 DEBUGMSG( "PAUSE request at tick: %i\n" , midifile.playing_tick ); |
180 pthread_mutex_lock( &amidiplug_playing_mutex ); | 213 g_mutex_lock( amidiplug_playing_mutex ); |
181 /* this cond is used to avoid race conditions */ | |
182 while ( amidiplug_playing_status != AMIDIPLUG_PLAY ) | |
183 pthread_cond_wait( &amidiplug_playing_cond , &amidiplug_playing_mutex ); | |
184 amidiplug_playing_status = AMIDIPLUG_PAUSE; | 214 amidiplug_playing_status = AMIDIPLUG_PAUSE; |
185 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 215 g_mutex_unlock( amidiplug_playing_mutex ); |
186 | |
187 pthread_join( amidiplug_play_thread , NULL ); | |
188 if ( backend.autonomous_audio == FALSE ) | |
189 pthread_join( amidiplug_audio_thread , NULL ); | |
190 DEBUGMSG( "PAUSE activated (play thread joined)\n" , midifile.playing_tick ); | |
191 | |
192 if ( backend.autonomous_audio == FALSE ) | 216 if ( backend.autonomous_audio == FALSE ) |
193 playback->output->pause(paused); | 217 playback->output->pause(paused); |
194 | |
195 /* kill the sequencer */ | |
196 backend.seq_off(); | |
197 } | 218 } |
198 else | 219 else |
199 { | 220 { |
200 DEBUGMSG( "PAUSE deactivated, returning to tick %i\n" , midifile.playing_tick ); | 221 DEBUGMSG( "PAUSE deactivated, resume playing from tick %i\n" , midifile.playing_tick ); |
201 /* revive the sequencer */ | |
202 backend.seq_on(); | |
203 /* re-set initial tempo */ | |
204 i_midi_setget_tempo( &midifile ); | |
205 backend.seq_queue_tempo( midifile.current_tempo , midifile.ppq ); | |
206 /* get back to the previous state */ | |
207 amidiplug_skipto( midifile.playing_tick ); | |
208 | |
209 if ( backend.autonomous_audio == FALSE ) | 222 if ( backend.autonomous_audio == FALSE ) |
210 playback->output->pause(paused); | 223 playback->output->pause(paused); |
211 | 224 g_mutex_lock( amidiplug_playing_mutex ); |
212 pthread_mutex_lock( &amidiplug_playing_mutex ); | 225 amidiplug_playing_status = AMIDIPLUG_PLAY; |
213 /* play play play! */ | 226 g_cond_signal( amidiplug_pause_cond ); |
214 DEBUGMSG( "PAUSE deactivated, starting play thread again\n" ); | 227 /* wait for unpause operation to be completed */ |
215 pthread_create(&amidiplug_play_thread, NULL, amidiplug_play_loop, playback); | 228 g_cond_wait( amidiplug_pause_cond , amidiplug_playing_mutex ); |
216 /* this cond is used to avoid race conditions */ | 229 g_mutex_unlock( amidiplug_playing_mutex ); |
217 while ( amidiplug_playing_status != AMIDIPLUG_PLAY ) | |
218 pthread_cond_wait( &amidiplug_playing_cond , &amidiplug_playing_mutex ); | |
219 pthread_mutex_unlock( &amidiplug_playing_mutex ); | |
220 } | 230 } |
221 } | 231 } |
222 | 232 |
223 | 233 |
224 static void amidiplug_seek( InputPlayback * playback, gint time ) | 234 static void amidiplug_seek( InputPlayback * playback, gint time ) |
225 { | 235 { |
226 DEBUGMSG( "SEEK requested (time %i), pausing song...\n" , time ); | 236 DEBUGMSG( "SEEK requested (time %i)...\n" , time ); |
227 pthread_mutex_lock( &amidiplug_playing_mutex ); | 237 g_mutex_lock( amidiplug_playing_mutex ); |
228 /* this cond is used to avoid race conditions */ | 238 midifile.seeking_tick = (gint)((time * 1000000) / midifile.avg_microsec_per_tick); |
229 while ( amidiplug_playing_status != AMIDIPLUG_PLAY ) | 239 amidiplug_playing_status = AMIDIPLUG_SEEK; |
230 pthread_cond_wait( &amidiplug_playing_cond , &amidiplug_playing_mutex ); | 240 g_mutex_unlock( amidiplug_playing_mutex ); |
231 amidiplug_playing_status = AMIDIPLUG_PAUSE; | |
232 pthread_mutex_unlock( &amidiplug_playing_mutex ); | |
233 | |
234 pthread_join( amidiplug_play_thread , NULL ); | |
235 if ( backend.autonomous_audio == FALSE ) | |
236 pthread_join( amidiplug_audio_thread , NULL ); | |
237 DEBUGMSG( "SEEK requested (time %i), song paused\n" , time ); | |
238 /* kill the sequencer */ | |
239 backend.seq_off(); | |
240 /* revive the sequencer */ | |
241 backend.seq_on(); | |
242 /* re-set initial tempo */ | |
243 i_midi_setget_tempo( &midifile ); | |
244 backend.seq_queue_tempo( midifile.current_tempo , midifile.ppq ); | |
245 /* get back to the previous state */ | |
246 DEBUGMSG( "SEEK requested (time %i), moving to tick %i of %i\n" , | |
247 time , (gint)((time * 1000000) / midifile.avg_microsec_per_tick) , midifile.max_tick ); | |
248 midifile.playing_tick = (gint)((time * 1000000) / midifile.avg_microsec_per_tick); | |
249 amidiplug_skipto( midifile.playing_tick ); | |
250 | 241 |
251 if ( backend.autonomous_audio == FALSE ) | 242 if ( backend.autonomous_audio == FALSE ) |
252 playback->output->flush(time * 1000); | 243 playback->output->flush(time * 1000); |
253 | |
254 /* play play play! */ | |
255 DEBUGMSG( "SEEK done, starting play thread again\n" ); | |
256 pthread_create(&amidiplug_play_thread, NULL, amidiplug_play_loop, playback); | |
257 } | 244 } |
258 | 245 |
259 | 246 |
260 static gint amidiplug_get_time( InputPlayback *playback ) | 247 static gint amidiplug_get_time( InputPlayback *playback ) |
261 { | 248 { |
262 if ( backend.autonomous_audio == FALSE ) | 249 if ( backend.autonomous_audio == FALSE ) |
263 { | 250 { |
264 pthread_mutex_lock( &amidiplug_playing_mutex ); | 251 g_mutex_lock( amidiplug_playing_mutex ); |
265 if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) || | 252 if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) || |
266 ( amidiplug_playing_status == AMIDIPLUG_PAUSE ) || | 253 ( amidiplug_playing_status == AMIDIPLUG_PAUSE ) || |
254 ( amidiplug_playing_status == AMIDIPLUG_SEEK ) || | |
267 (( amidiplug_playing_status == AMIDIPLUG_STOP ) && ( playback->output->buffer_playing() ))) | 255 (( amidiplug_playing_status == AMIDIPLUG_STOP ) && ( playback->output->buffer_playing() ))) |
268 { | 256 { |
269 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 257 g_mutex_unlock( amidiplug_playing_mutex ); |
270 return playback->output->output_time(); | 258 return playback->output->output_time(); |
271 } | 259 } |
272 else if ( amidiplug_playing_status == AMIDIPLUG_STOP ) | 260 else if ( amidiplug_playing_status == AMIDIPLUG_STOP ) |
273 { | 261 { |
274 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 262 g_mutex_unlock( amidiplug_playing_mutex ); |
275 DEBUGMSG( "GETTIME on stopped song, returning -1\n" , time ); | 263 DEBUGMSG( "GETTIME on stopped song, returning -1\n" , time ); |
276 return -1; | 264 return -1; |
277 } | 265 } |
278 else /* AMIDIPLUG_ERR */ | 266 else /* AMIDIPLUG_ERR */ |
279 { | 267 { |
280 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 268 g_mutex_unlock( amidiplug_playing_mutex ); |
281 DEBUGMSG( "GETTIME on halted song (an error occurred?), returning -1 and stopping the player\n" ); | 269 DEBUGMSG( "GETTIME on halted song (an error occurred?), returning -1 and stopping the player\n" ); |
282 audacious_drct_stop(); | 270 audacious_drct_stop(); |
283 return -1; | 271 return -1; |
284 } | 272 } |
285 } | 273 } |
286 else | 274 else |
287 { | 275 { |
288 gint pt; | 276 gint pt; |
289 pthread_mutex_lock( &amidiplug_playing_mutex ); | 277 g_mutex_lock( amidiplug_playing_mutex ); |
290 if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) || | 278 if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) || |
291 ( amidiplug_playing_status == AMIDIPLUG_PAUSE )) | 279 ( amidiplug_playing_status == AMIDIPLUG_PAUSE ) || |
292 { | 280 ( amidiplug_playing_status == AMIDIPLUG_SEEK )) |
293 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 281 { |
294 pthread_mutex_lock(&amidiplug_gettime_mutex); | 282 g_mutex_unlock( amidiplug_playing_mutex ); |
283 g_mutex_lock( amidiplug_gettime_mutex ); | |
295 pt = midifile.playing_tick; | 284 pt = midifile.playing_tick; |
296 pthread_mutex_unlock(&amidiplug_gettime_mutex); | 285 g_mutex_unlock( amidiplug_gettime_mutex ); |
297 return (gint)((pt * midifile.avg_microsec_per_tick) / 1000); | 286 return (gint)((pt * midifile.avg_microsec_per_tick) / 1000); |
298 } | 287 } |
299 else if ( amidiplug_playing_status == AMIDIPLUG_STOP ) | 288 else if ( amidiplug_playing_status == AMIDIPLUG_STOP ) |
300 { | 289 { |
301 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 290 g_mutex_unlock( amidiplug_playing_mutex ); |
302 DEBUGMSG( "GETTIME on stopped song, returning -1\n" , time ); | 291 DEBUGMSG( "GETTIME on stopped song, returning -1\n" , time ); |
303 return -1; | 292 return -1; |
304 } | 293 } |
305 else /* AMIDIPLUG_ERR */ | 294 else /* AMIDIPLUG_ERR */ |
306 { | 295 { |
307 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 296 g_mutex_unlock( amidiplug_playing_mutex ); |
308 DEBUGMSG( "GETTIME on halted song (an error occurred?), returning -1 and stopping the player\n" , time ); | 297 DEBUGMSG( "GETTIME on halted song (an error occurred?), returning -1 and stopping the player\n" , time ); |
309 audacious_drct_stop(); | 298 audacious_drct_stop(); |
310 return -1; | 299 return -1; |
311 } | 300 } |
312 } | 301 } |
473 (gint)(midifile.length / 1000) , | 462 (gint)(midifile.length / 1000) , |
474 au_bitdepth * au_samplerate * au_channels / 8 , | 463 au_bitdepth * au_samplerate * au_channels / 8 , |
475 au_samplerate , au_channels ); | 464 au_samplerate , au_channels ); |
476 g_free( filename ); | 465 g_free( filename ); |
477 | 466 |
467 /* done with file */ | |
468 VFS_FCLOSE( midifile.file_pointer ); | |
469 midifile.file_pointer = NULL; | |
470 | |
478 /* play play play! */ | 471 /* play play play! */ |
479 DEBUGMSG( "PLAY requested, starting play thread\n" ); | 472 DEBUGMSG( "PLAY requested, starting play thread\n" ); |
473 g_mutex_lock( amidiplug_playing_mutex ); | |
480 amidiplug_playing_status = AMIDIPLUG_PLAY; | 474 amidiplug_playing_status = AMIDIPLUG_PLAY; |
481 pthread_create(&amidiplug_play_thread, NULL, amidiplug_play_loop, playback); | 475 g_mutex_unlock( amidiplug_playing_mutex ); |
476 amidiplug_play_thread = g_thread_self(); | |
477 amidiplug_play_loop(playback); | |
482 break; | 478 break; |
483 } | 479 } |
484 | 480 |
485 default: | 481 default: |
486 { | 482 { |
488 g_warning( "%s is not a Standard MIDI File\n" , filename_uri ); | 484 g_warning( "%s is not a Standard MIDI File\n" , filename_uri ); |
489 break; | 485 break; |
490 } | 486 } |
491 } | 487 } |
492 | 488 |
493 VFS_FCLOSE( midifile.file_pointer ); | 489 if ( midifile.file_pointer ) |
494 } | 490 { |
495 | 491 /* done with file */ |
496 | 492 VFS_FCLOSE( midifile.file_pointer ); |
497 | 493 midifile.file_pointer = NULL; |
498 void * amidiplug_play_loop( void * arg ) | 494 } |
495 } | |
496 | |
497 | |
498 | |
499 gpointer amidiplug_play_loop( gpointer arg ) | |
499 { | 500 { |
500 InputPlayback *playback = arg; | 501 InputPlayback *playback = arg; |
501 gint i = 0; | 502 gint j = 0; |
502 gboolean rewind = FALSE; | 503 gboolean rewind = TRUE; |
503 | |
504 pthread_mutex_lock( &amidiplug_playing_mutex ); | |
505 if ( amidiplug_playing_status != AMIDIPLUG_PAUSE ) | |
506 { | |
507 DEBUGMSG( "PLAY thread, rewind tracks to their first event\n" ); | |
508 rewind = TRUE; | |
509 } | |
510 else | |
511 { | |
512 DEBUGMSG( "PLAY thread, do not rewind tracks to their first event (coming from a PAUSE status)\n" ); | |
513 amidiplug_playing_status = AMIDIPLUG_PLAY; | |
514 pthread_cond_signal( &amidiplug_playing_cond ); | |
515 } | |
516 pthread_mutex_unlock( &amidiplug_playing_mutex ); | |
517 | 504 |
518 if ( rewind ) | 505 if ( rewind ) |
519 { | 506 { |
520 /* initialize current position in each track */ | 507 /* initialize current position in each track */ |
521 for (i = 0; i < midifile.num_tracks; ++i) | 508 for (j = 0; j < midifile.num_tracks; ++j) |
522 midifile.tracks[i].current_event = midifile.tracks[i].first_event; | 509 midifile.tracks[j].current_event = midifile.tracks[j].first_event; |
523 backend.seq_queue_start(); | |
524 } | 510 } |
525 | 511 |
526 if ( backend.autonomous_audio == FALSE ) | 512 if ( backend.autonomous_audio == FALSE ) |
527 { | 513 amidiplug_audio_thread = g_thread_create(amidiplug_audio_loop, playback, TRUE, NULL); |
528 pthread_create(&amidiplug_audio_thread, NULL, amidiplug_audio_loop, playback); | 514 |
529 } | 515 /* queue start */ |
530 | 516 backend.seq_queue_start(); |
531 /* common settings for all our events */ | 517 /* common settings for all our events */ |
532 backend.seq_event_init(); | 518 backend.seq_event_init(); |
533 | 519 |
534 DEBUGMSG( "PLAY thread, start the play loop\n" ); | 520 DEBUGMSG( "PLAY thread, start the play loop\n" ); |
535 for (;;) | 521 for (;;) |
536 { | 522 { |
537 midievent_t * event = NULL; | 523 midievent_t * event = NULL; |
538 midifile_track_t * event_track = NULL; | 524 midifile_track_t * event_track = NULL; |
539 gint i, min_tick = midifile.max_tick + 1; | 525 gint i, min_tick = midifile.max_tick + 1; |
526 | |
527 /* check if the song has been paused/seeked/stopped */ | |
528 g_mutex_lock( amidiplug_playing_mutex ); | |
529 if ( amidiplug_playing_status != AMIDIPLUG_PLAY ) | |
530 { | |
531 DEBUGMSG( "PLAY thread, PAUSE/SEEK/STOP requested, handle in play loop\n" ); | |
532 if ( amidiplug_playing_status == AMIDIPLUG_PAUSE ) | |
533 { | |
534 DEBUGMSG( "PLAY thread, PAUSE requested, shut notes and make the play loop wait\n" ); | |
535 backend.seq_event_allnoteoff( midifile.playing_tick ); | |
536 backend.seq_queue_stop(); | |
537 while (( amidiplug_playing_status != AMIDIPLUG_PLAY ) && | |
538 ( amidiplug_playing_status != AMIDIPLUG_STOP )) | |
539 g_cond_wait( amidiplug_pause_cond , amidiplug_playing_mutex ); | |
540 DEBUGMSG( "PLAY thread, UNPAUSE requested, resume playing\n" ); | |
541 g_mutex_lock( amidiplug_gettime_mutex ); | |
542 midifile.skip_offset = midifile.playing_tick; | |
543 g_mutex_unlock( amidiplug_gettime_mutex ); | |
544 if ( backend.autonomous_audio == FALSE ) | |
545 amidiplug_audio_thread = g_thread_create(amidiplug_audio_loop, playback, TRUE, NULL); | |
546 backend.seq_queue_start(); | |
547 g_cond_signal( amidiplug_pause_cond ); | |
548 } | |
549 if ( amidiplug_playing_status == AMIDIPLUG_SEEK ) | |
550 { | |
551 backend.seq_event_allnoteoff( midifile.playing_tick ); | |
552 backend.seq_queue_stop(); | |
553 amidiplug_skipto( midifile.seeking_tick ); | |
554 midifile.seeking_tick = -1; | |
555 amidiplug_playing_status = AMIDIPLUG_PLAY; | |
556 g_cond_signal( amidiplug_seekonpause_cond ); | |
557 } | |
558 if ( amidiplug_playing_status == AMIDIPLUG_STOP ) | |
559 { | |
560 DEBUGMSG( "PLAY thread, STOP requested, stopping...\n" ); | |
561 g_mutex_unlock( amidiplug_playing_mutex ); | |
562 break; /* exit from the for (;;) loop */ | |
563 } | |
564 } | |
565 g_mutex_unlock( amidiplug_playing_mutex ); | |
540 | 566 |
541 /* search next event */ | 567 /* search next event */ |
542 for (i = 0; i < midifile.num_tracks; ++i) | 568 for (i = 0; i < midifile.num_tracks; ++i) |
543 { | 569 { |
544 midifile_track_t * track = &midifile.tracks[i]; | 570 midifile_track_t * track = &midifile.tracks[i]; |
549 event = e2; | 575 event = e2; |
550 event_track = track; | 576 event_track = track; |
551 } | 577 } |
552 } | 578 } |
553 | 579 |
554 /* check if the song has been stopped */ | |
555 pthread_mutex_lock( &amidiplug_playing_mutex ); | |
556 if ( amidiplug_playing_status != AMIDIPLUG_PLAY ) | |
557 { | |
558 DEBUGMSG( "PLAY thread, PAUSE or STOP requested, exiting from play loop\n" ); | |
559 event = NULL; | |
560 } | |
561 pthread_mutex_unlock( &amidiplug_playing_mutex ); | |
562 | |
563 if (!event) | 580 if (!event) |
564 break; /* end of song reached */ | 581 break; /* end of song reached */ |
565 | 582 |
566 /* advance pointer to next event */ | 583 /* advance pointer to next event */ |
567 event_track->current_event = event->next; | 584 event_track->current_event = event->next; |
597 break; | 614 break; |
598 case SND_SEQ_EVENT_TEMPO: | 615 case SND_SEQ_EVENT_TEMPO: |
599 backend.seq_event_tempo( event ); | 616 backend.seq_event_tempo( event ); |
600 DEBUGMSG( "PLAY thread, processing tempo event with value %i on tick %i\n" , | 617 DEBUGMSG( "PLAY thread, processing tempo event with value %i on tick %i\n" , |
601 event->data.tempo , event->tick ); | 618 event->data.tempo , event->tick ); |
602 pthread_mutex_lock(&amidiplug_gettime_mutex); | 619 g_mutex_lock( amidiplug_gettime_mutex ); |
603 midifile.current_tempo = event->data.tempo; | 620 midifile.current_tempo = event->data.tempo; |
604 pthread_mutex_unlock(&amidiplug_gettime_mutex); | 621 g_mutex_unlock( amidiplug_gettime_mutex ); |
605 break; | 622 break; |
606 case SND_SEQ_EVENT_META_TEXT: | 623 case SND_SEQ_EVENT_META_TEXT: |
607 /* do nothing */ | 624 /* do nothing */ |
608 break; | 625 break; |
609 case SND_SEQ_EVENT_META_LYRIC: | 626 case SND_SEQ_EVENT_META_LYRIC: |
612 default: | 629 default: |
613 DEBUGMSG( "PLAY thread, encountered invalid event type %i\n" , event->type ); | 630 DEBUGMSG( "PLAY thread, encountered invalid event type %i\n" , event->type ); |
614 break; | 631 break; |
615 } | 632 } |
616 | 633 |
617 pthread_mutex_lock(&amidiplug_gettime_mutex); | 634 g_mutex_lock( amidiplug_gettime_mutex ); |
618 midifile.playing_tick = event->tick; | 635 midifile.playing_tick = event->tick; |
619 pthread_mutex_unlock(&amidiplug_gettime_mutex); | 636 g_mutex_unlock( amidiplug_gettime_mutex ); |
620 | 637 |
621 if ( backend.autonomous_audio == TRUE ) | 638 if ( backend.autonomous_audio == TRUE ) |
622 { | 639 { |
623 /* these backends deal with audio production themselves (i.e. ALSA) */ | 640 /* these backends deal with audio production themselves (i.e. ALSA) */ |
624 backend.seq_output( NULL , NULL ); | 641 backend.seq_output( NULL , NULL ); |
625 } | 642 } |
626 } | 643 } |
627 | 644 |
628 backend.seq_output_shut( midifile.max_tick , midifile.skip_offset ); | 645 backend.seq_output_shut( midifile.max_tick , midifile.skip_offset ); |
629 | 646 |
630 pthread_mutex_lock( &amidiplug_playing_mutex ); | 647 g_mutex_lock( amidiplug_playing_mutex ); |
631 if ( amidiplug_playing_status != AMIDIPLUG_PAUSE ) | 648 if ( amidiplug_playing_status != AMIDIPLUG_PAUSE ) |
632 { | 649 { |
633 amidiplug_playing_status = AMIDIPLUG_STOP; | 650 amidiplug_playing_status = AMIDIPLUG_STOP; |
634 DEBUGMSG( "PLAY thread, song stopped/ended\n" ); | 651 DEBUGMSG( "PLAY thread, song stopped/ended\n" ); |
635 } | 652 } |
636 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 653 g_mutex_unlock( amidiplug_playing_mutex ); |
637 | 654 return NULL; |
638 pthread_exit(NULL); | |
639 } | 655 } |
640 | 656 |
641 | 657 |
642 /* amidigplug_skipto: re-do all events that influence the playing of our | 658 /* amidigplug_skipto: re-do all events that influence the playing of our |
643 midi file; re-do them using a time-tick of 0, so they are processed | 659 midi file; re-do them using a time-tick of 0, so they are processed |
722 case SND_SEQ_EVENT_SYSEX: | 738 case SND_SEQ_EVENT_SYSEX: |
723 backend.seq_event_sysex( event ); | 739 backend.seq_event_sysex( event ); |
724 break; | 740 break; |
725 case SND_SEQ_EVENT_TEMPO: | 741 case SND_SEQ_EVENT_TEMPO: |
726 backend.seq_event_tempo( event ); | 742 backend.seq_event_tempo( event ); |
727 pthread_mutex_lock(&amidiplug_gettime_mutex); | 743 g_mutex_lock( amidiplug_gettime_mutex ); |
728 midifile.current_tempo = event->data.tempo; | 744 midifile.current_tempo = event->data.tempo; |
729 pthread_mutex_unlock(&amidiplug_gettime_mutex); | 745 g_mutex_unlock( amidiplug_gettime_mutex ); |
730 break; | 746 break; |
731 } | 747 } |
732 | 748 |
733 if ( backend.autonomous_audio == TRUE ) | 749 if ( backend.autonomous_audio == TRUE ) |
734 { | 750 { |
741 | 757 |
742 return; | 758 return; |
743 } | 759 } |
744 | 760 |
745 | 761 |
746 void * amidiplug_audio_loop( void * arg ) | 762 gpointer amidiplug_audio_loop( gpointer arg ) |
747 { | 763 { |
748 InputPlayback *playback = arg; | 764 InputPlayback *playback = arg; |
749 gboolean going = 1; | 765 gboolean going = 1; |
750 gpointer buffer = NULL; | 766 gpointer buffer = NULL; |
751 gint buffer_size = 0; | 767 gint buffer_size = 0; |
756 while( ( playback->output->buffer_free() < buffer_size ) && ( going == TRUE ) ) | 772 while( ( playback->output->buffer_free() < buffer_size ) && ( going == TRUE ) ) |
757 G_USLEEP(10000); | 773 G_USLEEP(10000); |
758 produce_audio( playback->output->written_time() , | 774 produce_audio( playback->output->written_time() , |
759 FMT_S16_NE , 2 , buffer_size , buffer , &going ); | 775 FMT_S16_NE , 2 , buffer_size , buffer , &going ); |
760 } | 776 } |
761 pthread_mutex_lock( &amidiplug_playing_mutex ); | 777 g_mutex_lock( amidiplug_playing_mutex ); |
762 if ( amidiplug_playing_status != AMIDIPLUG_PLAY ) | 778 if (( amidiplug_playing_status != AMIDIPLUG_PLAY ) && |
779 ( amidiplug_playing_status != AMIDIPLUG_SEEK )) | |
763 going = FALSE; | 780 going = FALSE; |
764 pthread_mutex_unlock( &amidiplug_playing_mutex ); | 781 g_mutex_unlock( amidiplug_playing_mutex ); |
765 } | 782 } |
766 if ( buffer != NULL ) | 783 if ( buffer != NULL ) |
767 g_free( buffer ); | 784 g_free( buffer ); |
768 pthread_exit(NULL); | 785 return NULL; |
769 } | 786 } |