1/* FluidSynth - A Software Synthesizer
2 *
3 * Copyright (C) 2003  Peter Hanappe and others.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public License
7 * as published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 * 02111-1307, USA
19 */
20
21#include "fluid_midi.h"
22#include "fluid_sys.h"
23#include "fluid_synth.h"
24#include "fluid_settings.h"
25
26/* all outgoing user messages are stored in a global text buffer */
27#define MIDI_MESSAGE_LENGTH 1024
28char midi_message_buffer[MIDI_MESSAGE_LENGTH];
29
30
31/* Taken from Nagano Daisuke's USB-MIDI driver */
32
33static int remains_f0f6[] = {
34	0,	/** 0xF0 **/
35	2,	/** 0XF1 **/
36	3,	/** 0XF2 **/
37	2,	/** 0XF3 **/
38	2,	/** 0XF4 (Undefined by MIDI Spec, and subject to change) **/
39	2,	/** 0XF5 (Undefined by MIDI Spec, and subject to change) **/
40	1	/** 0XF6 **/
41};
42
43static int remains_80e0[] = {
44	3,	/** 0x8X Note Off **/
45	3,	/** 0x9X Note On **/
46	3,	/** 0xAX Poly-key pressure **/
47	3,	/** 0xBX Control Change **/
48	2,	/** 0xCX Program Change **/
49	2,	/** 0xDX Channel pressure **/
50	3 	/** 0xEX PitchBend Change **/
51};
52
53
54/***************************************************************
55 *
56 *                      MIDIFILE
57 */
58
59/**
60 * Open a MIDI file and return a new MIDI file handle.
61 * @internal
62 * @param filename Path of file to open.
63 * @return New MIDI file handle or NULL on error.
64 */
65fluid_midi_file* new_fluid_midi_file(char* filename)
66{
67	fluid_midi_file* mf;
68
69	mf = FLUID_NEW(fluid_midi_file);
70	if (mf == NULL) {
71		FLUID_LOG(FLUID_ERR, "Out of memory");
72		return NULL;
73	}
74	FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file));
75
76	mf->c = -1;
77	mf->running_status = -1;
78	mf->fp = FLUID_FOPEN(filename, "rb");
79
80	if (mf->fp == NULL) {
81		FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file");
82		FLUID_FREE(mf);
83		return NULL;
84	}
85
86	if (fluid_midi_file_read_mthd(mf) != FLUID_OK) {
87		FLUID_FREE(mf);
88		return NULL;
89	}
90	return mf;
91}
92
93/**
94 * Delete a MIDI file handle.
95 * @internal
96 * @param mf MIDI file handle to close and free.
97 */
98void delete_fluid_midi_file(fluid_midi_file* mf)
99{
100	if (mf == NULL) {
101		return;
102	}
103	if (mf->fp != NULL) {
104		FLUID_FCLOSE(mf->fp);
105	}
106	FLUID_FREE(mf);
107	return;
108}
109
110/*
111 * Get the next byte in a MIDI file.
112 */
113int fluid_midi_file_getc(fluid_midi_file* mf)
114{
115	unsigned char c;
116	int n;
117	if (mf->c >= 0) {
118		c = mf->c;
119		mf->c = -1;
120	} else {
121		n = FLUID_FREAD(&c, 1, 1, mf->fp);
122		mf->trackpos++;
123	}
124	return (int) c;
125}
126
127/*
128 * fluid_midi_file_push
129 */
130int fluid_midi_file_push(fluid_midi_file* mf, int c)
131{
132	mf->c = c;
133	return FLUID_OK;
134}
135
136/*
137 * fluid_midi_file_read
138 */
139int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len)
140{
141	int num = FLUID_FREAD(buf, 1, len, mf->fp);
142	mf->trackpos += num;
143#if DEBUG
144	if (num != len) {
145		FLUID_LOG(FLUID_DBG, "Coulnd't read the requested number of bytes");
146	}
147#endif
148	return (num != len)? FLUID_FAILED : FLUID_OK;
149}
150
151/*
152 * fluid_midi_file_skip
153 */
154int fluid_midi_file_skip(fluid_midi_file* mf, int skip)
155{
156	int err = FLUID_FSEEK(mf->fp, skip, SEEK_CUR);
157	if (err) {
158		FLUID_LOG(FLUID_ERR, "Failed to seek position in file");
159		return FLUID_FAILED;
160	}
161	return FLUID_OK;
162}
163
164/*
165 * fluid_midi_file_read_mthd
166 */
167int fluid_midi_file_read_mthd(fluid_midi_file* mf)
168{
169	char mthd[15];
170	if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) {
171		return FLUID_FAILED;
172	}
173	if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) || (mthd[9] > 2)) {
174		FLUID_LOG(FLUID_ERR, "Doesn't look like a MIDI file: invalid MThd header");
175		return FLUID_FAILED;
176	}
177	mf->type = mthd[9];
178	mf->ntracks = (unsigned) mthd[11];
179	mf->ntracks += (unsigned int) (mthd[10]) << 16;
180	if((mthd[12]) < 0){
181		mf->uses_smpte = 1;
182		mf->smpte_fps = -mthd[12];
183		mf->smpte_res = (unsigned) mthd[13];
184		FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet");
185		return FLUID_FAILED;
186	} else {
187		mf->uses_smpte = 0;
188		mf->division = (mthd[12] << 8) | (mthd[13] & 0xff);
189		FLUID_LOG(FLUID_DBG, "Division=%d", mf->division);
190	}
191	return FLUID_OK;
192}
193
194/*
195 * fluid_midi_file_load_tracks
196 */
197int fluid_midi_file_load_tracks(fluid_midi_file* mf, fluid_player_t* player)
198{
199	int i;
200	for (i = 0; i < mf->ntracks; i++) {
201		if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) {
202			return FLUID_FAILED;
203		}
204	}
205	return FLUID_OK;
206}
207
208/*
209 * fluid_isasciistring
210 */
211int fluid_isasciistring(char* s)
212{
213	int i;
214	int len = (int) FLUID_STRLEN(s);
215	for (i = 0; i < len; i++) {
216		if (!fluid_isascii(s[i])) {
217			return 0;
218		}
219	}
220	return 1;
221}
222
223/*
224 * fluid_getlength
225 */
226long fluid_getlength(unsigned char *s)
227{
228	long i = 0;
229	i = s[3] | (s[2]<<8) | (s[1]<<16) | (s[0]<<24);
230	return i;
231}
232
233/*
234 * fluid_midi_file_read_tracklen
235 */
236int fluid_midi_file_read_tracklen(fluid_midi_file* mf)
237{
238	unsigned char length[5];
239	if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
240		return FLUID_FAILED;
241	}
242	mf->tracklen = fluid_getlength(length);
243	mf->trackpos = 0;
244	mf->eot = 0;
245	return FLUID_OK;
246}
247
248/*
249 * fluid_midi_file_eot
250 */
251int fluid_midi_file_eot(fluid_midi_file* mf)
252{
253#if DEBUG
254	if (mf->trackpos > mf->tracklen) {
255		printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen);
256	}
257#endif
258	return mf->eot || (mf->trackpos >= mf->tracklen);
259}
260
261/*
262 * fluid_midi_file_read_track
263 */
264int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num)
265{
266	fluid_track_t* track;
267	unsigned char id[5], length[5];
268	int found_track = 0;
269	int skip;
270
271	if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) {
272		return FLUID_FAILED;
273	}
274	id[4]='\0';
275	mf->dtime = 0;
276
277	while (!found_track){
278
279		if (fluid_isasciistring((char*) id) == 0) {
280			FLUID_LOG(FLUID_ERR, "An non-ascii track header found, currupt file");
281			return FLUID_FAILED;
282
283		} else if (strcmp((char*) id, "MTrk") == 0) {
284
285			found_track = 1;
286
287			if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) {
288				return FLUID_FAILED;
289			}
290
291			track = new_fluid_track(num);
292			if (track == NULL) {
293				FLUID_LOG(FLUID_ERR, "Out of memory");
294				return FLUID_FAILED;
295			}
296
297			while (!fluid_midi_file_eot(mf)) {
298				if (fluid_midi_file_read_event(mf, track) != FLUID_OK) {
299					return FLUID_FAILED;
300				}
301			}
302
303			fluid_player_add_track(player, track);
304
305		} else {
306			found_track = 0;
307			if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
308				return FLUID_FAILED;
309			}
310			skip = fluid_getlength(length);
311/*        fseek(mf->fp, skip, SEEK_CUR); */
312			if (fluid_midi_file_skip(mf, skip) != FLUID_OK) {
313				return FLUID_FAILED;
314			}
315		}
316	}
317	if (feof(mf->fp)) {
318		FLUID_LOG(FLUID_ERR, "Unexpected end of file");
319		return FLUID_FAILED;
320	}
321	return FLUID_OK;
322}
323
324/*
325 * fluid_midi_file_read_varlen
326 */
327int fluid_midi_file_read_varlen(fluid_midi_file* mf)
328{
329	int i;
330	int c;
331	mf->varlen = 0;
332	for (i = 0;;i++) {
333		if (i == 4) {
334			FLUID_LOG(FLUID_ERR, "Invalid variable length number");
335			return FLUID_FAILED;
336		}
337		c = fluid_midi_file_getc(mf);
338		if (c < 0) {
339			FLUID_LOG(FLUID_ERR, "Unexpected end of file");
340			return FLUID_FAILED;
341		}
342		if (c & 0x80){
343			mf->varlen |= (int) (c & 0x7F);
344			mf->varlen <<= 7;
345		} else {
346			mf->varlen += c;
347			break;
348		}
349	}
350	return FLUID_OK;
351}
352
353/*
354 * fluid_midi_file_read_event
355 */
356int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track)
357{
358	int status;
359	int type;
360	int tempo;
361	unsigned char* metadata = NULL;
362	unsigned char* dyn_buf = NULL;
363	unsigned char static_buf[256];
364	int nominator, denominator, clocks, notes, sf, mi;
365	fluid_midi_event_t* evt;
366	int channel = 0;
367	int param1 = 0;
368	int param2 = 0;
369
370	/* read the delta-time of the event */
371	if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
372		return FLUID_FAILED;
373	}
374	mf->dtime += mf->varlen;
375
376	/* read the status byte */
377	status = fluid_midi_file_getc(mf);
378	if (status < 0) {
379		FLUID_LOG(FLUID_ERR, "Unexpected end of file");
380		return FLUID_FAILED;
381	}
382
383	/* not a valid status byte: use the running status instead */
384	if ((status & 0x80) == 0) {
385		if ((mf->running_status & 0x80) == 0) {
386			FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status");
387			return FLUID_FAILED;
388		}
389		fluid_midi_file_push(mf, status);
390		status = mf->running_status;
391	}
392
393	/* check what message we have */
394	if (status & 0x80) {
395		mf->running_status = status;
396
397		if ((status == MIDI_SYSEX) || (status == MIDI_EOX)) {     /* system exclusif */
398			/*
399			 * Sysex messages are not handled yet
400			 */
401			/* read the length of the message */
402			if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
403				return FLUID_FAILED;
404			}
405
406			if (mf->varlen) {
407
408				if (mf->varlen < 255) {
409					metadata = &static_buf[0];
410				} else {
411					FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen);
412					dyn_buf = FLUID_MALLOC(mf->varlen + 1);
413					if (dyn_buf == NULL) {
414						FLUID_LOG(FLUID_PANIC, "Out of memory");
415						return FLUID_FAILED;
416					}
417					metadata = dyn_buf;
418				}
419
420				/* read the data of the message */
421				if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
422					if (dyn_buf) {
423						FLUID_FREE(dyn_buf);
424					}
425					return FLUID_FAILED;
426				}
427
428				if (dyn_buf) {
429					FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__);
430					FLUID_FREE(dyn_buf);
431				}
432			}
433
434			return FLUID_OK;
435
436		} else if (status == MIDI_META_EVENT) {             /* meta events */
437
438			int result = FLUID_OK;
439
440			/* get the type of the meta message */
441			type = fluid_midi_file_getc(mf);
442			if (type < 0) {
443				FLUID_LOG(FLUID_ERR, "Unexpected end of file");
444				return FLUID_FAILED;
445			}
446
447			/* get the length of the data part */
448			if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
449				return FLUID_FAILED;
450			}
451
452			if (mf->varlen < 255) {
453				metadata = &static_buf[0];
454			} else {
455				FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen);
456				dyn_buf = FLUID_MALLOC(mf->varlen + 1);
457				if (dyn_buf == NULL) {
458					FLUID_LOG(FLUID_PANIC, "Out of memory");
459					return FLUID_FAILED;
460				}
461				metadata = dyn_buf;
462			}
463
464			/* read the data */
465			if (mf->varlen)
466			{
467				if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
468					if (dyn_buf) {
469						FLUID_FREE(dyn_buf);
470					}
471					return FLUID_FAILED;
472				}
473			}
474
475			/* handle meta data */
476			switch (type) {
477
478			case MIDI_COPYRIGHT:
479				metadata[mf->varlen] = 0;
480				break;
481
482			case MIDI_TRACK_NAME:
483				metadata[mf->varlen] = 0;
484				fluid_track_set_name(track, (char*) metadata);
485				break;
486
487			case MIDI_INST_NAME:
488				metadata[mf->varlen] = 0;
489				break;
490
491			case MIDI_LYRIC:
492				break;
493
494			case MIDI_MARKER:
495				break;
496
497			case MIDI_CUE_POINT:
498				break; /* don't care much for text events */
499
500			case MIDI_EOT:
501				if (mf->varlen != 0) {
502					FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event");
503					result = FLUID_FAILED;
504					break;
505				}
506				mf->eot = 1;
507				break;
508
509			case MIDI_SET_TEMPO:
510				if (mf->varlen != 3) {
511					FLUID_LOG(FLUID_ERR, "Invalid length for SetTempo meta event");
512					result = FLUID_FAILED;
513					break;
514				}
515				tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2];
516				evt = new_fluid_midi_event();
517				if (evt == NULL) {
518					FLUID_LOG(FLUID_ERR, "Out of memory");
519					result = FLUID_FAILED;
520					break;
521				}
522				evt->dtime = mf->dtime;
523				evt->type = MIDI_SET_TEMPO;
524				evt->channel = 0;
525				evt->param1 = tempo;
526				evt->param2 = 0;
527				fluid_track_add_event(track, evt);
528				mf->dtime = 0;
529				break;
530
531			case MIDI_SMPTE_OFFSET:
532				if (mf->varlen != 5) {
533					FLUID_LOG(FLUID_ERR, "Invalid length for SMPTE Offset meta event");
534					result = FLUID_FAILED;
535					break;
536				}
537				break; /* we don't use smtp */
538
539			case MIDI_TIME_SIGNATURE:
540				if (mf->varlen != 4) {
541					FLUID_LOG(FLUID_ERR, "Invalid length for TimeSignature meta event");
542					result = FLUID_FAILED;
543					break;
544				}
545				nominator = metadata[0];
546				denominator = pow(2.0, (double) metadata[1]);
547				clocks = metadata[2];
548				notes = metadata[3];
549
550				FLUID_LOG(FLUID_DBG, "signature=%d/%d, metronome=%d, 32nd-notes=%d",
551					  nominator, denominator, clocks, notes);
552
553				break;
554
555			case MIDI_KEY_SIGNATURE:
556				if (mf->varlen != 2) {
557					FLUID_LOG(FLUID_ERR, "Invalid length for KeySignature meta event");
558					result = FLUID_FAILED;
559					break;
560				}
561				sf = metadata[0];
562				mi = metadata[1];
563				break;
564
565			case MIDI_SEQUENCER_EVENT:
566				break;
567
568			default:
569				break;
570			}
571
572			if (dyn_buf) {
573				FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__);
574				FLUID_FREE(dyn_buf);
575			}
576
577			return result;
578
579		} else {                /* channel messages */
580
581			type = status & 0xf0;
582			channel = status & 0x0f;
583
584			/* all channel message have at least 1 byte of associated data */
585			if ((param1 = fluid_midi_file_getc(mf)) < 0) {
586				FLUID_LOG(FLUID_ERR, "Unexpected end of file");
587				return FLUID_FAILED;
588			}
589
590			switch (type) {
591
592			case NOTE_ON:
593				if ((param2 = fluid_midi_file_getc(mf)) < 0) {
594					FLUID_LOG(FLUID_ERR, "Unexpected end of file");
595					return FLUID_FAILED;
596				}
597				break;
598
599			case NOTE_OFF:
600				if ((param2 = fluid_midi_file_getc(mf)) < 0) {
601					FLUID_LOG(FLUID_ERR, "Unexpected end of file");
602					return FLUID_FAILED;
603				}
604				break;
605
606			case KEY_PRESSURE:
607				if ((param2 = fluid_midi_file_getc(mf)) < 0) {
608					FLUID_LOG(FLUID_ERR, "Unexpected end of file");
609					return FLUID_FAILED;
610				}
611				break;
612
613			case CONTROL_CHANGE:
614				if ((param2 = fluid_midi_file_getc(mf)) < 0) {
615					FLUID_LOG(FLUID_ERR, "Unexpected end of file");
616					return FLUID_FAILED;
617				}
618				break;
619
620			case PROGRAM_CHANGE:
621				break;
622
623			case CHANNEL_PRESSURE:
624				break;
625
626			case PITCH_BEND:
627				if ((param2 = fluid_midi_file_getc(mf)) < 0) {
628					FLUID_LOG(FLUID_ERR, "Unexpected end of file");
629					return FLUID_FAILED;
630				}
631
632				param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f);
633				param2 = 0;
634				break;
635
636			default:
637				/* Can't possibly happen !? */
638				FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event");
639				return FLUID_FAILED;
640			}
641			evt = new_fluid_midi_event();
642			if (evt == NULL) {
643				FLUID_LOG(FLUID_ERR, "Out of memory");
644				return FLUID_FAILED;
645			}
646			evt->dtime = mf->dtime;
647			evt->type = type;
648			evt->channel = channel;
649			evt->param1 = param1;
650			evt->param2 = param2;
651			fluid_track_add_event(track, evt);
652			mf->dtime = 0;
653		}
654	}
655	return FLUID_OK;
656}
657
658/*
659 * fluid_midi_file_get_division
660 */
661int fluid_midi_file_get_division(fluid_midi_file* midifile)
662{
663	return midifile->division;
664}
665
666/******************************************************
667 *
668 *     fluid_track_t
669 */
670
671/**
672 * Create a MIDI event structure.
673 * @return New MIDI event structure or NULL when out of memory.
674 */
675fluid_midi_event_t* new_fluid_midi_event()
676{
677	fluid_midi_event_t* evt;
678	evt = FLUID_NEW(fluid_midi_event_t);
679	if (evt == NULL) {
680		FLUID_LOG(FLUID_ERR, "Out of memory");
681		return NULL;
682	}
683	evt->dtime = 0;
684	evt->type = 0;
685	evt->channel = 0;
686	evt->param1 = 0;
687	evt->param2 = 0;
688	evt->next = NULL;
689	return evt;
690}
691
692/**
693 * Delete MIDI event structure.
694 * @param evt MIDI event structure
695 * @return Always returns 0
696 */
697int delete_fluid_midi_event(fluid_midi_event_t* evt)
698{
699	fluid_midi_event_t *temp;
700
701	while (evt)
702	{
703		temp = evt->next;
704		FLUID_FREE(evt);
705		evt = temp;
706	}
707	return FLUID_OK;
708}
709
710/**
711 * Get the event type field of a MIDI event structure.
712 * DOCME - Event type enum appears to be internal (fluid_midi.h)
713 * @param evt MIDI event structure
714 * @return Event type field
715 */
716int fluid_midi_event_get_type(fluid_midi_event_t* evt)
717{
718	return evt->type;
719}
720
721/**
722 * Set the event type field of a MIDI event structure.
723 * DOCME - Event type enum appears to be internal (fluid_midi.h)
724 * @param evt MIDI event structure
725 * @param type Event type field
726 * @return Always returns 0
727 */
728int fluid_midi_event_set_type(fluid_midi_event_t* evt, int type)
729{
730	evt->type = type;
731	return FLUID_OK;
732}
733
734/**
735 * Get the channel field of a MIDI event structure.
736 * @param evt MIDI event structure
737 * @return Channel field
738 */
739int fluid_midi_event_get_channel(fluid_midi_event_t* evt)
740{
741	return evt->channel;
742}
743
744/**
745 * Set the channel field of a MIDI event structure.
746 * @param evt MIDI event structure
747 * @param chan MIDI channel field
748 * @return Always returns 0
749 */
750int fluid_midi_event_set_channel(fluid_midi_event_t* evt, int chan)
751{
752	evt->channel = chan;
753	return FLUID_OK;
754}
755
756/**
757 * Get the key field of a MIDI event structure.
758 * @param evt MIDI event structure
759 * @return MIDI note number (0-127)
760 */
761int fluid_midi_event_get_key(fluid_midi_event_t* evt)
762{
763	return evt->param1;
764}
765
766/**
767 * Set the key field of a MIDI event structure.
768 * @param evt MIDI event structure
769 * @param v MIDI note number (0-127)
770 * @return Always returns 0
771 */
772int fluid_midi_event_set_key(fluid_midi_event_t* evt, int v)
773{
774	evt->param1 = v;
775	return FLUID_OK;
776}
777
778/**
779 * Get the velocity field of a MIDI event structure.
780 * @param evt MIDI event structure
781 * @return MIDI velocity number (0-127)
782 */
783int fluid_midi_event_get_velocity(fluid_midi_event_t* evt)
784{
785	return evt->param2;
786}
787
788/**
789 * Set the velocity field of a MIDI event structure.
790 * @param evt MIDI event structure
791 * @param v MIDI velocity value
792 * @return Always returns 0
793 */
794int fluid_midi_event_set_velocity(fluid_midi_event_t* evt, int v)
795{
796	evt->param2 = v;
797	return FLUID_OK;
798}
799
800/**
801 * Get the control number of a MIDI event structure.
802 * @param evt MIDI event structure
803 * @return MIDI control number
804 */
805int fluid_midi_event_get_control(fluid_midi_event_t* evt)
806{
807	return evt->param1;
808}
809
810/**
811 * Set the control field of a MIDI event structure.
812 * @param evt MIDI event structure
813 * @param v MIDI control number
814 * @return Always returns 0
815 */
816int fluid_midi_event_set_control(fluid_midi_event_t* evt, int v)
817{
818	evt->param1 = v;
819	return FLUID_OK;
820}
821
822/**
823 * Get the value field from a MIDI event structure.
824 * @param evt MIDI event structure
825 * @return Value field
826 */
827int fluid_midi_event_get_value(fluid_midi_event_t* evt)
828{
829	return evt->param2;
830}
831
832/**
833 * Set the value field of a MIDI event structure.
834 * @param evt MIDI event structure
835 * @param v Value to assign
836 * @return Always returns 0
837 */
838int fluid_midi_event_set_value(fluid_midi_event_t* evt, int v)
839{
840	evt->param2 = v;
841	return FLUID_OK;
842}
843
844/**
845 * Get the program field of a MIDI event structure.
846 * @param evt MIDI event structure
847 * @return MIDI program number (0-127)
848 */
849int fluid_midi_event_get_program(fluid_midi_event_t* evt)
850{
851	return evt->param1;
852}
853
854/**
855 * Set the program field of a MIDI event structure.
856 * @param evt MIDI event structure
857 * @param val MIDI program number (0-127)
858 * @return Always returns 0
859 */
860int fluid_midi_event_set_program(fluid_midi_event_t* evt, int val)
861{
862	evt->param1 = val;
863	return FLUID_OK;
864}
865
866/**
867 * Get the pitch field of a MIDI event structure.
868 * @param evt MIDI event structure
869 * @return Pitch value (DOCME units?)
870 */
871int fluid_midi_event_get_pitch(fluid_midi_event_t* evt)
872{
873	return evt->param1;
874}
875
876/**
877 * Set the pitch field of a MIDI event structure.
878 * @param evt MIDI event structure
879 * @param val Pitch value (DOCME units?)
880 * @return Always returns 0
881 */
882int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val)
883{
884	evt->param1 = val;
885	return FLUID_OK;
886}
887
888/*
889 * fluid_midi_event_get_param1
890 */
891/* int fluid_midi_event_get_param1(fluid_midi_event_t* evt) */
892/* { */
893/*   return evt->param1; */
894/* } */
895
896/*
897 * fluid_midi_event_set_param1
898 */
899/* int fluid_midi_event_set_param1(fluid_midi_event_t* evt, int v) */
900/* { */
901/*   evt->param1 = v; */
902/*   return FLUID_OK; */
903/* } */
904
905/*
906 * fluid_midi_event_get_param2
907 */
908/* int fluid_midi_event_get_param2(fluid_midi_event_t* evt) */
909/* { */
910/*   return evt->param2; */
911/* } */
912
913/*
914 * fluid_midi_event_set_param2
915 */
916/* int fluid_midi_event_set_param2(fluid_midi_event_t* evt, int v) */
917/* { */
918/*   evt->param2 = v; */
919/*   return FLUID_OK; */
920/* } */
921
922/******************************************************
923 *
924 *     fluid_track_t
925 */
926
927/*
928 * new_fluid_track
929 */
930fluid_track_t* new_fluid_track(int num)
931{
932	fluid_track_t* track;
933	track = FLUID_NEW(fluid_track_t);
934	if (track == NULL) {
935		return NULL;
936	}
937	track->name = NULL;
938	track->num = num;
939	track->first = NULL;
940	track->cur = NULL;
941	track->last = NULL;
942	track->ticks = 0;
943	return track;
944}
945
946/*
947 * delete_fluid_track
948 */
949int delete_fluid_track(fluid_track_t* track)
950{
951	if (track->name != NULL) {
952		FLUID_FREE(track->name);
953	}
954	if (track->first != NULL) {
955		delete_fluid_midi_event(track->first);
956	}
957	FLUID_FREE(track);
958	return FLUID_OK;
959}
960
961/*
962 * fluid_track_set_name
963 */
964int fluid_track_set_name(fluid_track_t* track, char* name)
965{
966	int len;
967	if (track->name != NULL) {
968		FLUID_FREE(track->name);
969	}
970	if (name == NULL) {
971		track->name = NULL;
972		return FLUID_OK;
973	}
974	len = FLUID_STRLEN(name);
975	track->name = FLUID_MALLOC(len + 1);
976	if (track->name == NULL) {
977		FLUID_LOG(FLUID_ERR, "Out of memory");
978		return FLUID_FAILED;
979	}
980	FLUID_STRCPY(track->name, name);
981	return FLUID_OK;
982}
983
984/*
985 * fluid_track_get_name
986 */
987char* fluid_track_get_name(fluid_track_t* track)
988{
989	return track->name;
990}
991
992/*
993 * fluid_track_get_duration
994 */
995int fluid_track_get_duration(fluid_track_t* track)
996{
997	int time = 0;
998	fluid_midi_event_t* evt = track->first;
999	while (evt != NULL) {
1000		time += evt->dtime;
1001		evt = evt->next;
1002	}
1003	return time;
1004}
1005
1006/*
1007 * fluid_track_count_events
1008 */
1009int fluid_track_count_events(fluid_track_t* track, int* on, int* off)
1010{
1011	fluid_midi_event_t* evt = track->first;
1012	while (evt != NULL) {
1013		if (evt->type == NOTE_ON) {
1014			(*on)++;
1015		} else if (evt->type == NOTE_OFF) {
1016			(*off)++;
1017		}
1018		evt = evt->next;
1019	}
1020	return FLUID_OK;
1021}
1022
1023/*
1024 * fluid_track_add_event
1025 */
1026int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt)
1027{
1028	evt->next = NULL;
1029	if (track->first == NULL) {
1030		track->first = evt;
1031		track->cur = evt;
1032		track->last = evt;
1033	} else {
1034		track->last->next = evt;
1035		track->last = evt;
1036	}
1037	return FLUID_OK;
1038}
1039
1040/*
1041 * fluid_track_first_event
1042 */
1043fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track)
1044{
1045	track->cur = track->first;
1046	return track->cur;
1047}
1048
1049/*
1050 * fluid_track_next_event
1051 */
1052fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track)
1053{
1054	if (track->cur != NULL) {
1055		track->cur = track->cur->next;
1056	}
1057	return track->cur;
1058}
1059
1060/*
1061 * fluid_track_reset
1062 */
1063int
1064fluid_track_reset(fluid_track_t* track)
1065{
1066	track->ticks = 0;
1067	track->cur = track->first;
1068	return FLUID_OK;
1069}
1070
1071/*
1072 * fluid_track_send_events
1073 */
1074int
1075fluid_track_send_events(fluid_track_t* track,
1076			fluid_synth_t* synth,
1077			fluid_player_t* player,
1078			unsigned int ticks)
1079{
1080	int status = FLUID_OK;
1081	fluid_midi_event_t* event;
1082
1083	while (1) {
1084
1085		event = track->cur;
1086		if (event == NULL) {
1087			return status;
1088		}
1089
1090/* 		printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */
1091/* 		       track->num, */
1092/* 		       ticks, */
1093/* 		       track->ticks, */
1094/* 		       event->dtime, */
1095/* 		       track->ticks + event->dtime); */
1096
1097		if (track->ticks + event->dtime > ticks) {
1098			return status;
1099		}
1100
1101
1102		track->ticks += event->dtime;
1103		status = fluid_midi_send_event(synth, player, event);
1104		fluid_track_next_event(track);
1105
1106	}
1107	return status;
1108}
1109
1110/******************************************************
1111 *
1112 *     fluid_player
1113 */
1114
1115/**
1116 * Create a new MIDI player.
1117 * @param synth Fluid synthesizer instance to create player for
1118 * @return New MIDI player instance or NULL on error (out of memory)
1119 */
1120fluid_player_t* new_fluid_player(fluid_synth_t* synth)
1121{
1122	int i;
1123	fluid_player_t* player;
1124	player = FLUID_NEW(fluid_player_t);
1125	if (player == NULL) {
1126		FLUID_LOG(FLUID_ERR, "Out of memory");
1127		return NULL;
1128	}
1129	player->status = FLUID_PLAYER_READY;
1130	player->loop = 0;
1131	player->ntracks = 0;
1132	for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
1133		player->track[i] = NULL;
1134	}
1135	player->synth = synth;
1136	player->timer = NULL;
1137	player->playlist = NULL;
1138	player->current_file = NULL;
1139	player->division = 0;
1140	player->send_program_change = 1;
1141	player->miditempo = 480000;
1142	player->deltatime = 4.0;
1143	return player;
1144}
1145
1146/**
1147 * Delete a MIDI player instance.
1148 * @param player MIDI player instance
1149 * @return Always returns 0
1150 */
1151int delete_fluid_player(fluid_player_t* player)
1152{
1153	if (player == NULL) {
1154		return FLUID_OK;
1155	}
1156	fluid_player_stop(player);
1157	fluid_player_reset(player);
1158	FLUID_FREE(player);
1159	return FLUID_OK;
1160}
1161
1162int fluid_player_reset(fluid_player_t* player)
1163{
1164	int i;
1165
1166	for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
1167		if (player->track[i] != NULL) {
1168			delete_fluid_track(player->track[i]);
1169			player->track[i] = NULL;
1170		}
1171	}
1172	player->current_file = NULL;
1173	player->status = FLUID_PLAYER_READY;
1174	player->loop = 0;
1175	player->ntracks = 0;
1176	player->division = 0;
1177	player->send_program_change = 1;
1178	player->miditempo = 480000;
1179	player->deltatime = 4.0;
1180	return 0;
1181}
1182
1183/*
1184 * fluid_player_add_track
1185 */
1186int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track)
1187{
1188	if (player->ntracks < MAX_NUMBER_OF_TRACKS) {
1189		player->track[player->ntracks++] = track;
1190		return FLUID_OK;
1191	} else {
1192		return FLUID_FAILED;
1193	}
1194}
1195
1196/*
1197 * fluid_player_count_tracks
1198 */
1199int fluid_player_count_tracks(fluid_player_t* player)
1200{
1201	return player->ntracks;
1202}
1203
1204/*
1205 * fluid_player_get_track
1206 */
1207fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i)
1208{
1209	if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) {
1210		return player->track[i];
1211	} else {
1212		return NULL;
1213	}
1214}
1215
1216int fluid_player_add(fluid_player_t* player, char* midifile)
1217{
1218	char *s = FLUID_STRDUP(midifile);
1219	player->playlist = fluid_list_append(player->playlist, s);
1220	return 0;
1221}
1222
1223/*
1224 * fluid_player_load
1225 */
1226int fluid_player_load(fluid_player_t* player, char *filename)
1227{
1228	fluid_midi_file* midifile;
1229
1230	midifile = new_fluid_midi_file(filename);
1231	if (midifile == NULL) {
1232		return FLUID_FAILED;
1233	}
1234	player->division = fluid_midi_file_get_division(midifile);
1235
1236	/*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */
1237
1238	if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK){
1239		return FLUID_FAILED;
1240	}
1241	delete_fluid_midi_file(midifile);
1242	return FLUID_OK;
1243}
1244
1245/*
1246 * fluid_player_callback
1247 */
1248int fluid_player_callback(void* data, unsigned int msec)
1249{
1250	int i;
1251	int status = FLUID_PLAYER_DONE;
1252	fluid_player_t* player;
1253	fluid_synth_t* synth;
1254	player = (fluid_player_t*) data;
1255	synth = player->synth;
1256
1257	/* Load the next file if necessary */
1258	while (player->current_file == NULL) {
1259
1260		if (player->playlist == NULL) {
1261			return 0;
1262		}
1263
1264		fluid_player_reset(player);
1265
1266		player->current_file = fluid_list_get(player->playlist);
1267		player->playlist = fluid_list_next(player->playlist);
1268
1269		FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, player->current_file);
1270
1271		if (fluid_player_load(player, player->current_file) == FLUID_OK) {
1272
1273			player->begin_msec = msec;
1274			player->start_msec = msec;
1275			player->start_ticks = 0;
1276			player->cur_ticks = 0;
1277
1278			for (i = 0; i < player->ntracks; i++) {
1279				if (player->track[i] != NULL) {
1280					fluid_track_reset(player->track[i]);
1281				}
1282			}
1283
1284		} else {
1285			player->current_file = NULL;
1286		}
1287	}
1288
1289	player->cur_msec = msec;
1290	player->cur_ticks = (player->start_ticks +
1291			     (int) ((double) (player->cur_msec - player->start_msec) / player->deltatime));
1292
1293	for (i = 0; i < player->ntracks; i++) {
1294		if (!fluid_track_eot(player->track[i])) {
1295			status = FLUID_PLAYER_PLAYING;
1296			if (fluid_track_send_events(player->track[i], synth, player, player->cur_ticks) != FLUID_OK) {
1297				/* */
1298			}
1299		}
1300	}
1301
1302	player->status = status;
1303
1304	if (player->status == FLUID_PLAYER_DONE) {
1305		FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec",
1306			  __FILE__, __LINE__, (msec - player->begin_msec) / 1000.0);
1307		player->current_file = NULL;
1308	}
1309
1310	return 1;
1311}
1312
1313/**
1314 * Activates play mode for a MIDI player if not already playing.
1315 * @param player MIDI player instance
1316 * @return 0 on success, -1 on failure
1317 */
1318int fluid_player_play(fluid_player_t* player)
1319{
1320	if (player->status == FLUID_PLAYER_PLAYING) {
1321		return FLUID_OK;
1322	}
1323
1324	if (player->playlist == NULL) {
1325		return FLUID_OK;
1326	}
1327
1328	player->status = FLUID_PLAYER_PLAYING;
1329
1330	player->timer = new_fluid_timer((int) player->deltatime, fluid_player_callback,
1331					(void*) player, 1, 0);
1332	if (player->timer == NULL) {
1333		return FLUID_FAILED;
1334	}
1335	return FLUID_OK;
1336}
1337
1338/**
1339 * Stops a MIDI player.
1340 * @param player MIDI player instance
1341 * @return Always returns 0
1342 */
1343int fluid_player_stop(fluid_player_t* player)
1344{
1345	if (player->timer != NULL) {
1346		delete_fluid_timer(player->timer);
1347	}
1348	player->status = FLUID_PLAYER_DONE;
1349	player->timer = NULL;
1350	return FLUID_OK;
1351}
1352
1353/* FIXME - Looping seems to not actually be implemented? */
1354
1355/**
1356 * Enable looping of a MIDI player (DOCME - Does this actually work?)
1357 * @param player MIDI player instance
1358 * @param loop Value for looping (DOCME - What would this value be, boolean/time index?)
1359 * @return Always returns 0
1360 */
1361int fluid_player_set_loop(fluid_player_t* player, int loop)
1362{
1363	player->loop = loop;
1364	return FLUID_OK;
1365}
1366
1367/**
1368 * Set the tempo of a MIDI player.
1369 * @param player MIDI player instance
1370 * @param tempo Tempo to set playback speed to (DOCME - Units?)
1371 * @return Always returns 0
1372 *
1373 */
1374int fluid_player_set_midi_tempo(fluid_player_t* player, int tempo)
1375{
1376	player->miditempo = tempo;
1377	player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */
1378	player->start_msec = player->cur_msec;
1379	player->start_ticks = player->cur_ticks;
1380
1381	FLUID_LOG(FLUID_DBG,"tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d",
1382		  tempo, player->deltatime, player->cur_msec, player->cur_ticks);
1383
1384	return FLUID_OK;
1385}
1386
1387/**
1388 * Set the tempo of a MIDI player in beats per minute.
1389 * @param player MIDI player instance
1390 * @param bpm Tempo in beats per minute
1391 * @return Always returns 0
1392 */
1393int fluid_player_set_bpm(fluid_player_t* player, int bpm)
1394{
1395	return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm));
1396}
1397
1398/**
1399 * Wait for a MIDI player to terminate (when done playing).
1400 * @param player MIDI player instance
1401 * @return 0 on success, -1 otherwise
1402 *
1403 */
1404int fluid_player_join(fluid_player_t* player)
1405{
1406	return player->timer? fluid_timer_join(player->timer) : FLUID_OK;
1407}
1408
1409/************************************************************************
1410 *       MIDI PARSER
1411 *
1412 */
1413
1414/*
1415 * new_fluid_midi_parser
1416 */
1417fluid_midi_parser_t* new_fluid_midi_parser()
1418{
1419	fluid_midi_parser_t* parser;
1420	parser = FLUID_NEW(fluid_midi_parser_t);
1421	if (parser == NULL) {
1422		FLUID_LOG(FLUID_ERR, "Out of memory");
1423		return NULL;
1424	}
1425	parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */
1426	return parser;
1427}
1428
1429/*
1430 * delete_fluid_midi_parser
1431 */
1432int delete_fluid_midi_parser(fluid_midi_parser_t* parser)
1433{
1434	FLUID_FREE(parser);
1435	return FLUID_OK;
1436}
1437
1438/*
1439 * fluid_midi_parser_parse
1440 *
1441 * Purpose:
1442 * The MIDI byte stream is fed into the parser, one byte at a time.
1443 * As soon as the parser has recognized an event, it will return it.
1444 * Otherwise it returns NULL.
1445 */
1446fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c)
1447{
1448	/*********************************************************************/
1449	/* 'Process' system real-time messages                               */
1450	/*********************************************************************/
1451	/* There are not too many real-time messages that are of interest here.
1452	 * They can occur anywhere, even in the middle of a noteon message!
1453	 * Real-time range: 0xF8 .. 0xFF
1454	 * Note: Real-time does not affect (running) status.
1455	 */
1456	if (c >= 0xF8){
1457		if (c == MIDI_SYSTEM_RESET){
1458			parser->event.type = c;
1459			parser->status = 0; /* clear the status */
1460			return &parser->event;
1461		};
1462		return NULL;
1463	};
1464
1465	/*********************************************************************/
1466	/* 'Process' system common messages (again, just skip them)          */
1467	/*********************************************************************/
1468	/* There are no system common messages that are of interest here.
1469	 * System common range: 0xF0 .. 0xF7
1470	 */
1471
1472	if (c > 0xF0){
1473		/* MIDI specs say: To ignore a non-real-time message, just discard all data up to
1474		 * the next status byte.
1475		 * And our parser will ignore data that is received without a valid status.
1476		 * Note: system common cancels running status. */
1477		parser->status = 0;
1478		return NULL;
1479	};
1480
1481	/*********************************************************************/
1482	/* Process voice category messages:                                  */
1483	/*********************************************************************/
1484	/* Now that we have handled realtime and system common messages, only
1485	 * voice messages are left.
1486	 * Only a status byte has bit # 7 set.
1487	 * So no matter the status of the parser (in case we have lost sync),
1488	 * as soon as a byte >= 0x80 comes in, we are dealing with a status byte
1489	 * and start a new event.
1490	 */
1491
1492	if (c & 0x80){
1493		parser->channel = c & 0x0F;
1494		parser->status = c & 0xF0;
1495		/* The event consumes x bytes of data... (subtract 1 for the status byte) */
1496		parser->nr_bytes_total=fluid_midi_event_length(parser->status)-1;
1497		/* of which we have read 0 at this time. */
1498		parser->nr_bytes = 0;
1499		return NULL;
1500	};
1501
1502	/*********************************************************************/
1503	/* Process data                                                      */
1504	/*********************************************************************/
1505	/* If we made it this far, then the received char belongs to the data
1506	 * of the last event. */
1507	if (parser->status == 0){
1508		/* We are not interested in the event currently received.
1509		 * Discard the data. */
1510		return NULL;
1511	};
1512
1513	/* Store the first couple of bytes */
1514	if (parser->nr_bytes < FLUID_MIDI_PARSER_MAX_PAR){
1515		parser->p[parser->nr_bytes]=c;
1516	};
1517	parser->nr_bytes++;
1518
1519	/* Do we still need more data to get this event complete? */
1520	if (parser->nr_bytes < parser->nr_bytes_total){
1521		return NULL;
1522	};
1523
1524	/*********************************************************************/
1525	/* Send the event                                                    */
1526	/*********************************************************************/
1527	/* The event is ready-to-go.
1528	 * About 'running status':
1529	 * The MIDI protocol has a built-in compression mechanism. If several similar events are sent
1530	 * in-a-row, for example note-ons, then the event type is only sent once. For this case,
1531	 * the last event type (status) is remembered.
1532	 * We simply keep the status as it is, just reset
1533	 * the parameter counter. If another status byte comes in, it will overwrite the status. */
1534	parser->event.type = parser->status;
1535	parser->event.channel = parser->channel;
1536	parser->nr_bytes = 0; /* Related to running status! */
1537	switch (parser->status){
1538	case NOTE_OFF:
1539	case NOTE_ON:
1540	case KEY_PRESSURE:
1541	case CONTROL_CHANGE:
1542	case PROGRAM_CHANGE:
1543	case CHANNEL_PRESSURE:
1544		parser->event.param1 = parser->p[0]; /* For example key number */
1545		parser->event.param2 = parser->p[1]; /* For example velocity */
1546		break;
1547	case PITCH_BEND:
1548		/* Pitch-bend is transmitted with 14-bit precision. */
1549		parser->event.param1 = ((parser->p[1] << 7) | parser->p[0]); /* Note: '|' does here the same as '+' (no common bits), but might be faster */
1550		break;
1551	default:
1552		/* Unlikely */
1553		return NULL;
1554	};
1555	return &parser->event;
1556};
1557
1558/* Purpose:
1559 * Returns the length of the MIDI message starting with c.
1560 * Taken from Nagano Daisuke's USB-MIDI driver */
1561int fluid_midi_event_length(unsigned char event){
1562	if ( event < 0xf0 ) {
1563		return remains_80e0[((event-0x80)>>4)&0x0f];
1564	} else if ( event < 0xf7 ) {
1565		return remains_f0f6[event-0xf0];
1566	} else {
1567		return 1;
1568	}
1569}
1570
1571/************************************************************************
1572 *       fluid_midi_send_event
1573 *
1574 * This is a utility function that doesn't really belong to any class
1575 * or structure. It is called by fluid_midi_track and fluid_midi_device.
1576 */
1577int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* event)
1578{
1579	switch (event->type) {
1580	case NOTE_ON:
1581		if (fluid_synth_noteon(synth, event->channel, event->param1, event->param2) != FLUID_OK) {
1582			return FLUID_FAILED;
1583		}
1584		break;
1585	case NOTE_OFF:
1586		if (fluid_synth_noteoff(synth, event->channel, event->param1) != FLUID_OK) {
1587			return FLUID_FAILED;
1588		}
1589		break;
1590	case CONTROL_CHANGE:
1591		if (fluid_synth_cc(synth, event->channel, event->param1, event->param2) != FLUID_OK) {
1592			return FLUID_FAILED;
1593		}
1594		break;
1595	case MIDI_SET_TEMPO:
1596		if (player != NULL) {
1597			if (fluid_player_set_midi_tempo(player, event->param1) != FLUID_OK) {
1598				return FLUID_FAILED;
1599			}
1600		}
1601		break;
1602	case PROGRAM_CHANGE:
1603		if (fluid_synth_program_change(synth, event->channel, event->param1) != FLUID_OK) {
1604			return FLUID_FAILED;
1605		}
1606		break;
1607	case PITCH_BEND:
1608		if (fluid_synth_pitch_bend(synth, event->channel, event->param1) != FLUID_OK) {
1609			return FLUID_FAILED;
1610		}
1611		break;
1612	default:
1613		break;
1614	}
1615	return FLUID_OK;
1616}
1617