aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/include/sfx_iterator.h
blob: 1a453a7f26781fb3c9a2087749d6b45ab961d250 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/***************************************************************************
 sfx_iterator.h (C) 2002..04 Christoph Reichenbach

 This program may be modified and copied freely according to the terms of
 the GNU general public license (GPL), as long as the above copyright
 notice and the licensing information contained herein are preserved.

 Please refer to www.gnu.org for licensing details.

 This work is provided AS IS, without warranty of any kind, expressed or
 implied, including but not limited to the warranties of merchantibility,
 noninfringement, and fitness for a specific purpose. The author will not
 be held liable for any damage caused by this work or derivatives of it.

 By using this source code, you agree to the licensing terms as stated
 above.


 Please contact the maintainer for bug reports or inquiries.

 Current Maintainer:

		Christoph Reichenbach (CJR) [reichenb@colorado.edu]

***************************************************************************/
/* Song iterator declarations */

#ifndef _SCI_SFX_ITERATOR_H_
#define _SCI_SFX_ITERATOR_H_

#include "sci/include/sfx_pcm.h"
#include "sci/include/listener.h"

#define SI_FINISHED -1 /* Song finished playing */
#define SI_LOOP -2 /* Song just looped */
#define SI_ABSOLUTE_CUE -3 /* Found a song cue (absolute) */
#define SI_RELATIVE_CUE -4 /* Found a song cue (relative) */
#define SI_PCM -5 /* Found a PCM */
#define SI_IGNORE -6 /* This event got edited out by the remapper */
#define SI_MORPH -255 /* Song iterator requested self-morph. */

#define FADE_ACTION_NONE              0
#define FADE_ACTION_FADE_AND_STOP     1
#define FADE_ACTION_FADE_AND_CONT     2

typedef struct {
	int ticks_per_step;
	int final_volume;
	int step_size;
	int action;
} fade_params_t;

#define SONG_ITERATOR_MESSAGE_ARGUMENTS_NR 2

/* Helper defs for messages */
/* Base messages */
#define _SIMSG_BASE 0 /* Any base decoder */
#define _SIMSG_BASEMSG_SET_LOOPS 0 /* Set loops */
#define _SIMSG_BASEMSG_CLONE 1 /* Clone object and data. Must provide the
** (possibly negative) number of ticks that have
** passed since the last delay time started being
** used  */
#define _SIMSG_BASEMSG_SET_PLAYMASK 2 /* Set the current playmask for filtering */
#define _SIMSG_BASEMSG_SET_RHYTHM 3 /* Activate/deactivate rhythm channel */
#define _SIMSG_BASEMSG_ACK_MORPH 4 /* Acknowledge self-morph */
#define _SIMSG_BASEMSG_STOP 5 /* Stop iterator */
#define _SIMSG_BASEMSG_PRINT 6 /* Print self to stderr, after printing param1 tabs */
#define _SIMSG_BASEMSG_SET_HOLD 7 /* Set value of hold parameter to expect */
#define _SIMSG_BASEMSG_SET_FADE 8 /* Set fade parameters */

/* "Plastic" (discardable) wrapper messages */
#define _SIMSG_PLASTICWRAP 1 /* Any base decoder */
#define _SIMSG_PLASTICWRAP_ACK_MORPH 4 /* Acknowledge self-morph */

/* Messages */
#define SIMSG_SET_LOOPS(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_LOOPS,(x),0
#define SIMSG_SET_PLAYMASK(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_PLAYMASK,(x),0
#define SIMSG_SET_RHYTHM(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_RHYTHM,(x),0
#define SIMSG_CLONE(x) _SIMSG_BASE,_SIMSG_BASEMSG_CLONE,(x),0
#define SIMSG_ACK_MORPH _SIMSG_PLASTICWRAP,_SIMSG_PLASTICWRAP_ACK_MORPH,0,0
#define SIMSG_STOP _SIMSG_BASE,_SIMSG_BASEMSG_STOP,0,0
#define SIMSG_PRINT(indentation) _SIMSG_BASE,_SIMSG_BASEMSG_PRINT,(indentation),0
#define SIMSG_SET_HOLD(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_HOLD,(x),0
/*#define SIMSG_SET_FADE(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_FADE,(x),0*/

/* Message transmission macro: Takes song reference, message reference */
#define SIMSG_SEND(o, m) songit_handle_message(&(o), songit_make_message((o)->ID, m))
#define SIMSG_SEND_FADE(o, m) songit_handle_message(&(o), songit_make_ptr_message((o)->ID, _SIMSG_BASE, _SIMSG_BASEMSG_SET_FADE, m, 0))


typedef unsigned long songit_id_t;

typedef struct {
	songit_id_t ID;
	unsigned int recipient; /* Type of iterator supposed to receive this */
	unsigned int type;
	union {
		unsigned int i;
		void * p;
	} args[SONG_ITERATOR_MESSAGE_ARGUMENTS_NR];
} song_iterator_message_t;

#define INHERITS_SONG_ITERATOR \
	songit_id_t ID;										  \
	guint16 channel_mask;									  \
        fade_params_t fade;                                                                       \
	unsigned int flags;									  \
	int priority;                                                                             \
	int (*next) (song_iterator_t *self, unsigned char *buf, int *buf_size);			  \
	sfx_pcm_feed_t * (*get_pcm_feed) (song_iterator_t *s);					  \
	song_iterator_t * (* handle_message)(song_iterator_t *self, song_iterator_message_t msg); \
	void (*init) (struct _song_iterator *self);						  \
	void (*cleanup) (struct _song_iterator *self);						  \
        int (*get_timepos) (struct _song_iterator *self);                                         \
	listener_t death_listeners[SONGIT_MAX_LISTENERS];					  \
	int death_listeners_nr									  \
 
#define SONGIT_MAX_LISTENERS 2

typedef struct _song_iterator {

	songit_id_t ID;
	guint16 channel_mask; /* Bitmask of all channels this iterator will use */
	fade_params_t fade;
	unsigned int flags;
	int priority;

	int (*next)(struct _song_iterator *self,
	            unsigned char *buf, int *result);
	/* Reads the next MIDI operation _or_ delta time
	** Parameters: (song_iterator_t *) self
	**             (byte *) buf: The buffer to write to (needs to be able to
	**                           store at least 4 bytes)
	** Returns   : (int) zero if a MIDI operation was written, SI_FINISHED
	**                   if the song has finished playing, SI_LOOP if looping
	**                   (after updating the loop variable), SI_CUE if we found
	**                   a cue, SI_PCM if a PCM was found, or the number of ticks
	**                   to wait before this function should be called next.
	**             (int) *result: Number of bytes written to the buffer
	**                   (equals the number of bytes that need to be passed
	**                   to the lower layers) for 0, the cue value for SI_CUE,
	**                   or the number of loops remaining for SI_LOOP.
	**   If SI_PCM is returned, get_pcm() may be used to retrieve the associated
	** PCM, but this must be done before any subsequent calls to next().
	*/

	sfx_pcm_feed_t * (*get_pcm_feed)(struct _song_iterator *self);
	/* Checks for the presence of a pcm sample
	** Parameters: (song_iterator_t *) self
	** Returns   : (sfx_pcm_feed_t *) NULL if no PCM data was found, a
	**				  PCM feed otherwise
	*/


	struct _song_iterator *
				(* handle_message)(struct _song_iterator *self, song_iterator_message_t msg);
	/* Handles a message to the song iterator
	** Parameters: (song_iterator_t *) self
	**             (song_iterator_messag_t) msg: The message to handle
	** Returns   : (song_iterator_t *) NULL if the message was not understood,
	**             self if the message could be handled, or a new song iterator
	**             if the current iterator had to be morphed (but the message could
	**             still be handled)
	** This function is not supposed to be called directly; use
	** songit_handle_message() instead. It should not recurse, since songit_handle_message()
	** takes care of that and makes sure that its delegate received the message (and
	** was morphed) before self.
	*/


	void (*init)(struct _song_iterator *self);
	/* Resets/initializes the sound iterator
	** Parameters: (song_iterator_t *) self
	** Returns   : (void)
	*/

	void (*cleanup)(struct _song_iterator *self);
	/* Frees any content of the iterator structure
	** Parameters: (song_iterator_t *) self
	** Does not physically free(self) yet. May be NULL if nothing needs to be done.
	** Must not recurse on its delegate.
	*/

	int (*get_timepos)(struct _song_iterator *self);
	/* Gets the song position to store in a savegame
	** Parameters: (song_iterator_t *) self
	*/

	/* Death listeners */
	/* These are not reset during initialisation */
	listener_t death_listeners[SONGIT_MAX_LISTENERS];
	int death_listeners_nr;

	/* See songit_* for the constructor and non-virtual member functions */

} song_iterator_t;


/* Song iterator flags */
#define SONGIT_FLAG_CLONE	(1 << 0)	/* This flag is set for clones, which are exclusively used in song players.
** Thus, this flag distinguishes song iterators in the main thread from those
** in the song-player thread. */

void
song_iterator_add_death_listener(song_iterator_t *it,
                                 void *client,
                                 void (*notify)(void *self, void *notifier));
/* Adds a death listener to a song iterator
** Parameters: (song_iterator_t *) it: The iterator to add to
**             (void *) client: The object wanting to be notified
**             (void* x void* -> void) notify: The notification function
**                                     to invoke
** Effects:    Fatally terminates the program if no listener slots are
**	       available
** Death listeners are NOT cloned.
*/

void
song_iterator_remove_death_listener(song_iterator_t *it,
                                    void *client);
/* Removes a death listener from a song iterator
** Parameters: (song_iterator_t *) it: The iterator to modify
**             (void *) client: The object no longer wanting to be notified
** Effects:    Fatally terminates the program if the listener was not
**	       found
** Death listeners are NOT cloned.
*/

/********************************/
/*-- Song iterator operations --*/
/********************************/

#define SCI_SONG_ITERATOR_TYPE_SCI0 0
#define SCI_SONG_ITERATOR_TYPE_SCI1 1

#define IT_READER_MASK_MIDI	(1 << 0)
#define IT_READER_MASK_DELAY	(1 << 1)
#define IT_READER_MASK_LOOP	(1 << 2)
#define IT_READER_MASK_CUE	(1 << 3)
#define IT_READER_MASK_PCM	(1 << 4)
#define IT_READER_MAY_FREE	(1 << 10) /* Free SI_FINISHED iterators */
#define IT_READER_MAY_CLEAN	(1 << 11)
/* MAY_CLEAN: May instantiate cleanup iterators
** (use for players; this closes open channels at the end of a song) */

#define IT_READER_MASK_ALL (  IT_READER_MASK_MIDI	\
			    | IT_READER_MASK_DELAY	\
			    | IT_READER_MASK_LOOP	\
			    | IT_READER_MASK_CUE	\
			    | IT_READER_MASK_PCM )

int
songit_next(song_iterator_t **it, unsigned char *buf, int *result, int mask);
/* Convenience wrapper around it->next
** Parameters: (song_iterator_t **it) Reference to the iterator to access
**             (byte *) buf: The buffer to write to (needs to be able to
**                           store at least 4 bytes)
**             (int) mask: IT_READER_MASK options specifying the events to
**                   listen for
** Returns   : (int) zero if a MIDI operation was written, SI_FINISHED
**                   if the song has finished playing, SI_LOOP if looping
**                   (after updating the loop variable), SI_CUE if we found
**                   a cue, SI_PCM if a PCM was found, or the number of ticks
**                   to wait before this function should be called next.
**             (int) *result: Number of bytes written to the buffer
**                   (equals the number of bytes that need to be passed
**                   to the lower layers) for 0, the cue value for SI_CUE,
**                   or the number of loops remaining for SI_LOOP.
*/

song_iterator_t *
songit_new(unsigned char *data, unsigned int size, int type, songit_id_t id);
/* Constructs a new song iterator object
** Parameters: (byte *) data: The song data to iterate over
**             (unsigned int) size: Number of bytes in the song
**             (int) type: One of the SCI_SONG_ITERATOR_TYPEs
**             (songit_id_t) id: An ID for addressing the song iterator
** Returns   : (song_iterator_t *) A newly allocated but uninitialized song
**             iterator, or NULL if 'type' was invalid or unsupported
*/

song_iterator_t *
songit_new_tee(song_iterator_t *left, song_iterator_t *right, int may_destroy);
/* Combines two iterators, returns the next event available from either
** Parameters: (song_iterator_t *) left: One of the iterators
**             (song_iterator_t *) right: The other iterator
**             (int) may_destroy: Whether completed song iterators may be
**                                destroyed
** Returns   : (song_iterator_t *) A combined iterator, as suggested above
*/


void
songit_free(song_iterator_t *it);
/* Frees a song iterator and the song it wraps
** Parameters: (song_iterator_t *) it: The song iterator to free
** Returns   : (void)
*/

song_iterator_message_t
songit_make_message(songit_id_t id,
                    int recipient_class, int type, int a1, int a2);
/* Create a song iterator message
** Parameters: (songit_id_t) id: song ID the message is targetted to
**             (int) recipient_class: Message recipient class
**             (int) type: Message type
**             (int x int) a1, a2: Arguments
** You should only use this with the SIMSG_* macros
*/

song_iterator_message_t
songit_make_ptr_message(songit_id_t id,
                        int recipient_class, int type, void * a1, int a2);
/* Create a song iterator message, wherein the first parameter is a pointer
** Parameters: (songit_id_t) id: song ID the message is targetted to
**             (int) recipient_class: Message recipient class
**             (int) type: Message type
**             (void* x int) a1, a2: Arguments
** You should only use this with the SIMSG_* macros
*/

int
songit_handle_message(song_iterator_t **it_reg, song_iterator_message_t msg);
/* Handles a message to the song iterator
** Parameters: (song_iterator_t **): A reference to the variable storing the song iterator
** Returns   : (int) Non-zero if the message was understood
** The song iterator may polymorph as result of msg, so a writeable reference is required.
*/


song_iterator_t *
songit_clone(song_iterator_t *it, int delta);
/* Clones a song iterator
** Parameters: (song_iterator_t *) it: The iterator to clone
**             (int) delta: Number of ticks that still need to elapse until
**                          the next item should be read from the song iterator
** Returns   : (song_iterator_t *) A shallow clone of 'it'.
** This performs a clone on the bottom-most part (containing the actual song data) _only_.
** The justification for requiring 'delta' to be passed in here is that this
** is typically maintained outside of the song iterator.
*/


int
sfx_play_iterator_pcm(song_iterator_t *it, unsigned long handle);
/* Plays a song iterator that found a PCM through a PCM device, if possible
** Parameters: (song_iterator_t *) it: The iterator to play
**             (song_handle_t) handle: Debug handle
** Returns   : (int) 0 if the effect will not be played, nonzero if it will
** This assumes that the last call to 'it->next()' returned SI_PCM.
*/

#endif