scummvm/engines/sci/sfx/iterator.cpp

2054 lines
54 KiB
C++

/***************************************************************************
iterator.c Copyright (C) 2001..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 (CR) <jameson@linuxgames.com>
***************************************************************************/
/* Song iterators */
#include <stdio.h>
#include "sci/include/sfx_iterator_internal.h"
#include "sci/include/sfx_player.h"
#include "sci/include/resource.h"
#include "sci/include/sci_memory.h"
static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 1, 1, 2, 0
};
/*#define DEBUG_DECODING*/
/*#define DEBUG_VERBOSE*/
void
print_tabs_id(int nr, songit_id_t id) {
while (nr-- > 0)
fprintf(stderr, "\t");
fprintf(stderr, "[%08lx] ", id);
}
#ifndef HAVE_MEMCHR
static void *
memchr(void *_data, int c, int n) {
unsigned char *data = (unsigned char *) _data;
while (n && !(*data == c)) {
++data;
--n;
}
if (n)
return data;
else
return NULL;
}
#endif
static void
_common_init(base_song_iterator_t *self) {
self->fade.action = FADE_ACTION_NONE;
self->resetflag = 0;
self->loops = 0;
self->priority = 0;
}
/************************************/
/*-- SCI0 iterator implementation --*/
/************************************/
#define SCI0_MIDI_OFFSET 33
#define SCI0_END_OF_SONG 0xfc /* proprietary MIDI command */
#define SCI0_PCM_SAMPLE_RATE_OFFSET 0x0e
#define SCI0_PCM_SIZE_OFFSET 0x20
#define SCI0_PCM_DATA_OFFSET 0x2c
#define CHECK_FOR_END_ABSOLUTE(offset) \
if (offset > self->size) { \
fprintf(stderr, SIPFX "Reached end of song without terminator (%x/%x) at %d!\n", offset, self->size, __LINE__); \
return SI_FINISHED; \
}
#define CHECK_FOR_END(offset_augment) \
if ((channel->offset + (offset_augment)) > channel->end) { \
channel->state = SI_STATE_FINISHED; \
fprintf(stderr, SIPFX "Reached end of track %d without terminator (%x+%x/%x) at %d!\n", channel->id, channel->offset, offset_augment, channel->end, __LINE__); \
return SI_FINISHED; \
}
static inline int
_parse_ticks(byte *data, int *offset_p, int size) {
int ticks = 0;
int tempticks;
int offset = 0;
do {
tempticks = data[offset++];
ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX) ?
SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks;
} while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX
&& offset < size);
if (offset_p)
*offset_p = offset;
return ticks;
}
static int
_sci0_read_next_command(sci0_song_iterator_t *self,
unsigned char *buf, int *result);
static int
_sci0_get_pcm_data(sci0_song_iterator_t *self,
sfx_pcm_config_t *format,
int *xoffset,
unsigned int *xsize);
#define PARSE_FLAG_LOOPS_UNLIMITED (1 << 0) /* Unlimited # of loops? */
#define PARSE_FLAG_PARAMETRIC_CUE (1 << 1) /* Assume that cues take an additional "cue value" argument */
/* This implements a difference between SCI0 and SCI1 cues. */
void
_reset_synth_channels(base_song_iterator_t *self, song_iterator_channel_t *channel) {
int i;
byte buf[5];
tell_synth_func *tell = sfx_get_player_tell_func();
for (i = 0; i < MIDI_CHANNELS; i++) {
if (channel->saw_notes & (1 << i)) {
buf[0] = 0xe0 | i; /* Pitch bend */
buf[1] = 0x80; /* Wheel center */
buf[2] = 0x40;
if (tell)
tell(3, buf);
/* TODO: Reset other controls? */
}
}
}
static int
_parse_sci_midi_command(base_song_iterator_t *self, unsigned char *buf, int *result,
song_iterator_channel_t *channel,
int flags) {
unsigned char cmd;
int paramsleft;
int midi_op;
int midi_channel;
channel->state = SI_STATE_DELTA_TIME;
cmd = self->data[channel->offset++];
if (!(cmd & 0x80)) {
/* 'Running status' mode */
channel->offset--;
cmd = channel->last_cmd;
}
if (cmd == 0xfe) {
fprintf(stderr, "song iterator subsystem: Corrupted sound resource detected.\n");
return SI_FINISHED;
}
midi_op = cmd >> 4;
midi_channel = cmd & 0xf;
paramsleft = MIDI_cmdlen[midi_op];
channel->saw_notes |= 1 << midi_channel;
#if 0
if (1) {
fprintf(stderr, "[IT]: off=%x, cmd=%02x, takes %d args ",
channel->offset - 1, cmd, paramsleft);
fprintf(stderr, "[%02x %02x <%02x> %02x %02x %02x]\n",
self->data[channel->offset-3],
self->data[channel->offset-2],
self->data[channel->offset-1],
self->data[channel->offset],
self->data[channel->offset+1],
self->data[channel->offset+2]);
}
#endif
buf[0] = cmd;
CHECK_FOR_END(paramsleft);
memcpy(buf + 1, self->data + channel->offset, paramsleft);
*result = 1 + paramsleft;
channel->offset += paramsleft;
channel->last_cmd = cmd;
/* Are we supposed to play this channel? */
if (
/* First, exclude "global" properties-- such as cues-- from consideration */
(midi_op < 0xf
&& !(cmd == SCI_MIDI_SET_SIGNAL)
&& !(SCI_MIDI_CONTROLLER(cmd)
&& buf[1] == SCI_MIDI_CUMULATIVE_CUE))
/* Next, check if the channel is allowed */
&& (!((1 << midi_channel) & channel->playmask)))
return /* Execute next command */
self->next((song_iterator_t *) self, buf, result);
if (cmd == SCI_MIDI_EOT) {
/* End of track? */
_reset_synth_channels(self, channel);
/* fprintf(stderr, "eot; loops = %d, notesplayed=%d\n", self->loops, channel->notes_played);*/
if (self->loops > 1 /* && channel->notes_played*/) {
/* If allowed, decrement the number of loops */
if (!(flags & PARSE_FLAG_LOOPS_UNLIMITED))
*result = --self->loops;
#ifdef DEBUG_DECODING
fprintf(stderr, "%s L%d: (%p):%d Looping ", __FILE__, __LINE__, self, channel->id);
if (flags & PARSE_FLAG_LOOPS_UNLIMITED)
fprintf(stderr, "(indef.)");
else
fprintf(stderr, "(%d)", self->loops);
fprintf(stderr, " %x -> %x\n",
channel->offset, channel->loop_offset);
#endif
channel->offset = channel->loop_offset;
channel->notes_played = 0;
channel->state = SI_STATE_DELTA_TIME;
channel->total_timepos = channel->loop_timepos;
channel->last_cmd = 0xfe;
fprintf(stderr, "Looping song iterator %08lx.\n", self->ID);
return SI_LOOP;
} else {
channel->state = SI_STATE_FINISHED;
#ifdef DEBUG_DECODING
fprintf(stderr, "%s L%d: (%p):%d EOT because"
" %d notes, %d loops\n",
__FILE__, __LINE__, self, channel->id,
channel->notes_played, self->loops);
#endif
return SI_FINISHED;
}
} else if (cmd == SCI_MIDI_SET_SIGNAL) {
if (buf[1] == SCI_MIDI_SET_SIGNAL_LOOP) {
channel->loop_offset = channel->offset;
channel->loop_timepos = channel->total_timepos;
return /* Execute next command */
self->next((song_iterator_t *) self, buf, result);
} else {
/* Used to be conditional <= 127 */
*result = buf[1]; /* Absolute cue */
return SI_ABSOLUTE_CUE;
}
} else if (SCI_MIDI_CONTROLLER(cmd)) {
switch (buf[1]) {
case SCI_MIDI_CUMULATIVE_CUE:
if (flags & PARSE_FLAG_PARAMETRIC_CUE)
self->ccc += buf[2];
else { /* No parameter to CC */
self->ccc++;
/* channel->offset--; */
}
*result = self->ccc;
return SI_RELATIVE_CUE;
case SCI_MIDI_RESET_ON_SUSPEND:
self->resetflag = buf[2];
break;
case SCI_MIDI_SET_POLYPHONY:
self->polyphony[midi_channel] = buf[2];
#if 0
{
int i;
int voices = 0;
for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++) {
voices += self->polyphony[i];
}
sciprintf("SET_POLYPHONY(%d, %d) for a total of %d voices\n", midi_channel, buf[2], voices);
sciprintf("[iterator-1] DEBUG: Polyphony = [ ");
for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++)
sciprintf("%d ", self->polyphony[i]);
sciprintf("]\n");
sciprintf("[iterator-1] DEBUG: Importance = [ ");
for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++)
sciprintf("%d ", self->importance[i]);
sciprintf("]\n");
}
#endif
break;
case SCI_MIDI_SET_REVERB:
break;
case SCI_MIDI_CHANNEL_MUTE:
sciprintf("CHANNEL_MUTE(%d, %d)\n", midi_channel, buf[2]);
break;
case SCI_MIDI_HOLD: {
// Safe cast: This controller is only used in SCI1
sci1_song_iterator_t *self1 = (sci1_song_iterator_t *) self;
if (buf[2] == self1->hold) {
channel->offset = channel->initial_offset;
channel->notes_played = 0;
channel->state = SI_STATE_COMMAND;
channel->total_timepos = 0;
self1->channels_looped = self1->active_channels - 1;
return SI_LOOP;
}
break;
}
case 0x04: /* UNKNOWN NYI (happens in LSL2 gameshow) */
case 0x46: /* UNKNOWN NYI (happens in LSL3 binoculars) */
case 0x61: /* UNKNOWN NYI (special for adlib? Iceman) */
case 0x73: /* UNKNOWN NYI (happens in Hoyle) */
case 0xd1: /* UNKNOWN NYI (happens in KQ4 when riding the unicorn) */
return /* Execute next command */
self->next((song_iterator_t *) self, buf, result);
case 0x01: /* modulation */
case 0x07: /* volume */
case 0x0a: /* panpot */
case 0x0b: /* expression */
case 0x40: /* hold */
case 0x79: /* reset all */
/* No special treatment neccessary */
break;
}
return 0;
} else {
if ((cmd & 0xf0) == 0x90) /* note on? */
channel->notes_played++;
/* Process as normal MIDI operation */
return 0;
}
}
static int
_sci_midi_process_state(base_song_iterator_t *self, unsigned char *buf, int *result,
song_iterator_channel_t *channel,
int flags) {
CHECK_FOR_END(0);
switch (channel->state) {
case SI_STATE_PCM: {
if (*(self->data + channel->offset) == 0
&& *(self->data + channel->offset + 1) == SCI_MIDI_EOT)
/* Fake one extra tick to trick the interpreter into not killing the song iterator right away */
channel->state = SI_STATE_PCM_MAGIC_DELTA;
else
channel->state = SI_STATE_DELTA_TIME;
return SI_PCM;
}
case SI_STATE_PCM_MAGIC_DELTA: {
sfx_pcm_config_t format;
int offset;
unsigned int size;
int delay;
if (_sci0_get_pcm_data((sci0_song_iterator_t *) self, &format, &offset, &size))
return SI_FINISHED; /* 'tis broken */
channel->state = SI_STATE_FINISHED;
delay = (size * 50 + format.rate - 1) / format.rate; /* number of ticks to completion*/
fprintf(stderr, "delaying %d ticks\n", delay);
return delay;
}
case SI_STATE_UNINITIALISED:
fprintf(stderr, SIPFX "Attempt to read command from uninitialized iterator!\n");
self->init((song_iterator_t *) self);
return self->next((song_iterator_t *) self, buf, result);
case SI_STATE_FINISHED:
return SI_FINISHED;
case SI_STATE_DELTA_TIME: {
int offset;
int ticks = _parse_ticks(self->data + channel->offset,
&offset,
self->size - channel->offset);
channel->offset += offset;
channel->delay += ticks;
channel->timepos_increment = ticks;
CHECK_FOR_END(0);
channel->state = SI_STATE_COMMAND;
if (ticks)
return ticks;
}
/* continute otherwise... */
case SI_STATE_COMMAND: {
int retval;
channel->total_timepos += channel->timepos_increment;
channel->timepos_increment = 0;
retval = _parse_sci_midi_command(self, buf, result,
channel, flags);
if (retval == SI_FINISHED) {
if (self->active_channels)
--(self->active_channels);
#ifdef DEBUG_DECODING
fprintf(stderr, "%s L%d: (%p):%d Finished channel, %d channels left\n",
__FILE__, __LINE__, self, channel->id,
self->active_channels);
#endif
/* If we still have channels left... */
if (self->active_channels) {
return self->next((song_iterator_t *) self, buf, result);
}
/* Otherwise, we have reached the end */
self->loops = 0;
}
return retval;
}
default:
fprintf(stderr, SIPFX "Invalid iterator state %d!\n",
channel->state);
BREAKPOINT();
return SI_FINISHED;
}
}
static inline int
_sci_midi_process(base_song_iterator_t *self, unsigned char *buf, int *result,
song_iterator_channel_t *channel,
int flags) {
return _sci_midi_process_state(self, buf, result,
channel,
flags);
}
static int
_sci0_read_next_command(sci0_song_iterator_t *self, unsigned char *buf,
int *result) {
return _sci_midi_process((base_song_iterator_t *) self, buf, result,
&(self->channel),
PARSE_FLAG_PARAMETRIC_CUE);
}
static inline int
_sci0_header_magic_p(unsigned char *data, int offset, int size) {
if (offset + 0x10 > size)
return 0;
return
(data[offset] == 0x1a)
&& (data[offset + 1] == 0x00)
&& (data[offset + 2] == 0x01)
&& (data[offset + 3] == 0x00);
}
static int
_sci0_get_pcm_data(sci0_song_iterator_t *self,
sfx_pcm_config_t *format,
int *xoffset,
unsigned int *xsize) {
int tries = 2;
int found_it = 0;
unsigned char *pcm_data;
int size;
unsigned int offset = SCI0_MIDI_OFFSET;
if (self->data[0] != 2)
return 1;
/* No such luck */
while ((tries--) && (offset < self->size) && (!found_it)) {
/* Search through the garbage manually */
unsigned char *fc = (unsigned char*)memchr(self->data + offset,
SCI0_END_OF_SONG,
self->size - offset);
if (!fc) {
fprintf(stderr, SIPFX "Warning: Playing unterminated"
" song!\n");
return 1;
}
/* add one to move it past the END_OF_SONG marker */
offset = fc - self->data + 1;
if (_sci0_header_magic_p(self->data, offset, self->size))
found_it = 1;
}
if (!found_it) {
fprintf(stderr, SIPFX
"Warning: Song indicates presence of PCM, but"
" none found (finally at offset %04x)\n", offset);
return 1;
}
pcm_data = self->data + offset;
size = getUInt16(pcm_data + SCI0_PCM_SIZE_OFFSET);
/* Two of the format parameters are fixed by design: */
format->format = SFX_PCM_FORMAT_U8;
format->stereo = SFX_PCM_MONO;
format->rate = getUInt16(pcm_data + SCI0_PCM_SAMPLE_RATE_OFFSET);
if (offset + SCI0_PCM_DATA_OFFSET + size != self->size) {
int d = offset + SCI0_PCM_DATA_OFFSET + size - self->size;
fprintf(stderr, SIPFX
"Warning: PCM advertizes %d bytes of data, but %d"
" bytes are trailing in the resource!\n",
size, self->size - (offset + SCI0_PCM_DATA_OFFSET));
if (d > 0)
size -= d; /* Fix this */
}
*xoffset = offset;
*xsize = size;
return 0;
}
static sfx_pcm_feed_t *
_sci0_check_pcm(sci0_song_iterator_t *self) {
sfx_pcm_config_t format;
int offset;
unsigned int size;
if (_sci0_get_pcm_data(self, &format, &offset, &size))
return NULL;
self->channel.state
= SI_STATE_FINISHED; /* Don't play both PCM and music */
return sfx_iterator_make_feed(self->data,
offset + SCI0_PCM_DATA_OFFSET,
size,
format);
}
static song_iterator_t *
_sci0_handle_message(sci0_song_iterator_t *self, song_iterator_message_t msg) {
if (msg.recipient == _SIMSG_BASE) {
switch (msg.type) {
case _SIMSG_BASEMSG_PRINT:
print_tabs_id(msg.args[0].i, self->ID);
fprintf(stderr, "SCI0: dev=%d, active-chan=%d, size=%d, loops=%d\n",
self->device_id, self->active_channels, self->size,
self->loops);
break;
case _SIMSG_BASEMSG_SET_LOOPS:
self->loops = msg.args[0].i;
break;
case _SIMSG_BASEMSG_CLONE: {
int tsize = sizeof(sci0_song_iterator_t);
base_song_iterator_t *mem = (base_song_iterator_t*)sci_malloc(tsize);
memcpy(mem, self, tsize);
sci_refcount_incref(mem->data);
#ifdef DEBUG_VERBOSE
fprintf(stderr, "** CLONE INCREF for new %p from %p at %p\n", mem, self, mem->data);
#endif
return (struct _song_iterator *) mem; /* Assume caller has another copy of this */
}
case _SIMSG_BASEMSG_STOP: {
songit_id_t sought_id = msg.ID;
if (sought_id == self->ID)
self->channel.state = SI_STATE_FINISHED;
break;
}
case _SIMSG_BASEMSG_SET_PLAYMASK: {
int i;
self->device_id = msg.args[0].i;
/* Set all but the rhytm channel mask bits */
self->channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
for (i = 0; i < MIDI_CHANNELS; i++)
if (self->data[2 + (i << 1)] & self->device_id
&& i != MIDI_RHYTHM_CHANNEL)
self->channel.playmask |= (1 << i);
}
break;
case _SIMSG_BASEMSG_SET_RHYTHM:
self->channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
if (msg.args[0].i)
self->channel.playmask |= (1 << MIDI_RHYTHM_CHANNEL);
break;
case _SIMSG_BASEMSG_SET_FADE: {
fade_params_t *fp = (fade_params_t *) msg.args[0].p;
self->fade.action = fp->action;
self->fade.final_volume = fp->final_volume;
self->fade.ticks_per_step = fp->ticks_per_step;
self->fade.step_size = fp->step_size;
break;
}
default:
return NULL;
}
return (song_iterator_t *)self;
}
return NULL;
}
static int
_sci0_get_timepos(sci0_song_iterator_t *self) {
return self->channel.total_timepos;
}
static void
_base_init_channel(song_iterator_channel_t *channel, int id, int offset,
int end) {
channel->playmask = PLAYMASK_NONE; /* Disable all channels */
channel->id = id;
channel->notes_played = 0;
channel->state = SI_STATE_DELTA_TIME;
channel->loop_timepos = 0;
channel->total_timepos = 0;
channel->timepos_increment = 0;
channel->delay = 0; /* Only used for more than one channel */
channel->last_cmd = 0xfe;
channel->offset
= channel->loop_offset
= channel->initial_offset
= offset;
channel->end = end;
channel->saw_notes = 0;
}
static void
_sci0_init(sci0_song_iterator_t *self) {
_common_init((base_song_iterator_t *) self);
self->ccc = 0; /* Reset cumulative cue counter */
self->active_channels = 1;
_base_init_channel(&(self->channel), 0, SCI0_MIDI_OFFSET, self->size);
_reset_synth_channels((base_song_iterator_t *) self,
&(self->channel));
self->delay_remaining = 0;
if (self->data[0] == 2) /* Do we have an embedded PCM? */
self->channel.state = SI_STATE_PCM;
}
static void
_sci0_cleanup(sci0_song_iterator_t *self) {
#ifdef DEBUG_VERBOSE
fprintf(stderr, "** FREEING it %p: data at %p\n", self, self->data);
#endif
if (self->data)
sci_refcount_decref(self->data);
self->data = NULL;
}
/***************************/
/*-- SCI1 song iterators --*/
/***************************/
#define SCI01_INVALID_DEVICE 0xff
/* First index determines whether DSP output is supported */
static int sci0_to_sci1_device_map[][2] = {
{0x06, 0x0c}, /* MT-32 */
{0xff, 0xff}, /* YM FB-01 */
{0x00, 0x00}, /* CMS/Game Blaster-- we assume OPL/2 here... */
{0xff, 0xff}, /* Casio MT540/CT460 */
{0x13, 0x13}, /* Tandy 3-voice */
{0x12, 0x12}, /* PC speaker */
{0xff, 0xff},
{0xff, 0xff},
}; /* Maps bit number to device ID */
#define SONGDATA(x) self->data[offset + (x)]
#define SCI1_CHANDATA(off) self->data[channel->offset + (off)]
static int
_sci1_sample_init(sci1_song_iterator_t *self, int offset) {
sci1_sample_t *sample, **seekerp;
int rate;
int length;
int begin;
int end;
CHECK_FOR_END_ABSOLUTE((unsigned int)offset + 10);
if (self->data[offset + 1] != 0)
sciprintf("[iterator-1] In sample at offset 0x04x: Byte #1 is %02x instead of zero\n",
self->data[offset + 1]);
rate = getInt16(self->data + offset + 2);
length = getUInt16(self->data + offset + 4);
begin = getInt16(self->data + offset + 6);
end = getInt16(self->data + offset + 8);
CHECK_FOR_END_ABSOLUTE((unsigned int)(offset + 10 + length));
sample = (sci1_sample_t*)sci_malloc(sizeof(sci1_sample_t));
sample->delta = begin;
sample->size = length;
sample->data = self->data + offset + 10;
#ifdef DEBUG_VERBOSE
fprintf(stderr, "[SAMPLE] %x/%x/%x/%x l=%x\n",
offset + 10, begin, end, self->size, length);
#endif
sample->format.format = SFX_PCM_FORMAT_U8;
sample->format.stereo = SFX_PCM_MONO;
sample->format.rate = rate;
sample->announced = 0;
/* Perform insertion sort */
seekerp = &(self->next_sample);
while (*seekerp && (*seekerp)->delta < begin)
seekerp = &((*seekerp)->next);
sample->next = *seekerp;
*seekerp = sample;
return 0; /* Everything's fine */
}
static int
_sci1_song_init(sci1_song_iterator_t *self) {
sci1_sample_t *seeker;
int last_time;
unsigned int offset = 0;
self->channels_nr = 0;
self->next_sample = 0;
// self->device_id = 0x0c;
CHECK_FOR_END_ABSOLUTE(0);
if (SONGDATA(0) == 0xf0) {
self->priority = SONGDATA(1);
offset += 8;
}
while (SONGDATA(0) != 0xff
&& SONGDATA(0) != self->device_id) {
offset++;
CHECK_FOR_END_ABSOLUTE(offset + 1);
while (SONGDATA(0) != 0xff) {
CHECK_FOR_END_ABSOLUTE(offset + 7);
offset += 6;
}
offset++;
}
if (SONGDATA(0) == 0xff) {
sciprintf("[iterator-1] Song does not support"
" hardware 0x%02x\n",
self->device_id);
return 1;
}
offset++;
while (SONGDATA(0) != 0xff) { /* End of list? */
unsigned int track_offset;
int end;
offset += 2;
CHECK_FOR_END_ABSOLUTE(offset + 4);
track_offset = getUInt16(self->data + offset);
end = getUInt16(self->data + offset + 2);
CHECK_FOR_END_ABSOLUTE(track_offset - 1);
if (self->data[track_offset] == 0xfe) {
if (_sci1_sample_init(self, track_offset))
return 1; /* Error */
} else {
/* Regular MIDI channel */
if (self->channels_nr >= MIDI_CHANNELS) {
sciprintf("[iterator-1] Warning: Song has more than %d channels, cutting them off\n",
MIDI_CHANNELS);
break; /* Scan for remaining samples */
} else {
int channel_nr
= self->data[track_offset] & 0xf;
song_iterator_channel_t *channel =
&(self->channels[self->channels_nr++]);
if (self->data[track_offset] & 0xf0)
printf("Channel %d has mapping bits %02x\n",
channel_nr, self->data[track_offset] & 0xf0);
_base_init_channel(channel,
channel_nr,
/* Skip over header bytes: */
track_offset + 2,
track_offset + end);
_reset_synth_channels((base_song_iterator_t *) self,
channel);
self->polyphony[self->channels_nr - 1]
= SCI1_CHANDATA(-1);
self->importance[self->channels_nr - 1]
= self->polyphony[self->channels_nr - 1] >> 4;
self->polyphony[self->channels_nr - 1] &= 15;
channel->playmask = ~0; /* Enable all */
self->channel_mask |= (1 << channel_nr);
CHECK_FOR_END_ABSOLUTE(offset + end);
}
}
offset += 4;
CHECK_FOR_END_ABSOLUTE(offset);
}
/* Now ensure that sapmle deltas are relative to the previous sample */
seeker = self->next_sample;
last_time = 0;
self->active_channels = self->channels_nr;
self->channels_looped = 0;
while (seeker) {
int prev_last_time = last_time;
sciprintf("[iterator-1] Detected sample: %d Hz, %d bytes at time %d\n",
seeker->format.rate, seeker->size, seeker->delta);
last_time = seeker->delta;
seeker->delta -= prev_last_time;
seeker = seeker->next;
}
return 0; /* Success */
}
#undef SONGDATA
static inline int
_sci1_get_smallest_delta(sci1_song_iterator_t *self) {
int i, d = -1;
for (i = 0; i < self->channels_nr; i++)
if (self->channels[i].state == SI_STATE_COMMAND
&& (d == -1 || self->channels[i].delay < d))
d = self->channels[i].delay;
if (self->next_sample && self->next_sample->delta < d)
return self->next_sample->delta;
else
return d;
}
static inline void
_sci1_update_delta(sci1_song_iterator_t *self, int delta) {
int i;
if (self->next_sample)
self->next_sample->delta -= delta;
for (i = 0; i < self->channels_nr; i++)
if (self->channels[i].state == SI_STATE_COMMAND)
self->channels[i].delay -= delta;
}
static inline int
_sci1_no_delta_time(sci1_song_iterator_t *self) { /* Checks that none of the channels is waiting for its delta to be read */
int i;
for (i = 0; i < self->channels_nr; i++)
if (self->channels[i].state == SI_STATE_DELTA_TIME)
return 0;
return 1;
}
#if 0
// Unreferenced - removed
static void
_sci1_dump_state(sci1_song_iterator_t *self) {
int i;
sciprintf("-- [%p] ------------------------\n", self);
for (i = 0; i < self->channels_nr; i++) {
int j;
sciprintf("%d(s%02d): d-%d:\t(%x/%x) ",
self->channels[i].id,
self->channels[i].state,
self->channels[i].delay,
self->channels[i].offset,
self->channels[i].end);
for (j = -3; j < 9; j++) {
if (j == 0)
sciprintf(">");
else
sciprintf(" ");
sciprintf("%02x", self->data[self->channels[i].offset + j]);
if (j == 0)
sciprintf("<");
else
sciprintf(" ");
}
sciprintf("\n");
}
if (self->next_sample) {
sciprintf("\t[sample %d]\n",
self->next_sample->delta);
}
sciprintf("------------------------------------------\n");
}
#endif
#define COMMAND_INDEX_NONE -1
#define COMMAND_INDEX_PCM -2
static inline int /* Determine the channel # of the next active event, or -1 */
_sci1_command_index(sci1_song_iterator_t *self) {
int i;
int base_delay = 0x7ffffff;
int best_chan = COMMAND_INDEX_NONE;
for (i = 0; i < self->channels_nr; i++)
if ((self->channels[i].state != SI_STATE_PENDING)
&& (self->channels[i].state != SI_STATE_FINISHED)) {
if ((self->channels[i].state == SI_STATE_DELTA_TIME)
&& (self->channels[i].delay == 0))
return i;
/* First, read all unknown delta times */
if (self->channels[i].delay < base_delay) {
best_chan = i;
base_delay = self->channels[i].delay;
}
}
if (self->next_sample && base_delay >= self->next_sample->delta)
return COMMAND_INDEX_PCM;
return best_chan;
}
static sfx_pcm_feed_t *
_sci1_get_pcm(sci1_song_iterator_t *self) {
if (self->next_sample
&& self->next_sample->delta <= 0) {
sci1_sample_t *sample = self->next_sample;
sfx_pcm_feed_t *feed
= sfx_iterator_make_feed(self->data,
sample->data - self->data,
sample->size,
sample->format);
self->next_sample = self->next_sample->next;
sci_free(sample);
return feed;
} else
return NULL;
}
static int
_sci1_process_next_command(sci1_song_iterator_t *self,
unsigned char *buf, int *result) {
int retval = -42; /* Shouldn't happen, but gcc doesn't agree */
int chan;
if (!self->initialised) {
sciprintf("[iterator-1] DEBUG: Initialising for %d\n",
self->device_id);
self->initialised = 1;
if (_sci1_song_init(self))
return SI_FINISHED;
}
if (self->delay_remaining) {
int delay = self->delay_remaining;
self->delay_remaining = 0;
return delay;
}
do {
chan = _sci1_command_index(self);
if (chan == COMMAND_INDEX_NONE) {
return SI_FINISHED;
}
if (chan == COMMAND_INDEX_PCM) {
if (self->next_sample->announced) {
/* Already announced; let's discard it */
sfx_pcm_feed_t *feed
= _sci1_get_pcm(self);
feed->destroy(feed);
} else {
int delay = self->next_sample->delta;
if (delay) {
_sci1_update_delta(self, delay);
return delay;
}
/* otherwise we're touching a PCM */
self->next_sample->announced = 1;
return SI_PCM;
}
} else { /* Not a PCM */
retval = _sci_midi_process((base_song_iterator_t *) self,
buf, result,
&(self->channels[chan]),
PARSE_FLAG_LOOPS_UNLIMITED);
if (retval == SI_LOOP) {
self->channels_looped++;
self->channels[chan].state = SI_STATE_PENDING;
self->channels[chan].delay = 0;
if (self->channels_looped == self->active_channels) {
int i;
/* Everyone's ready: Let's loop */
for (i = 0; i < self->channels_nr; i++)
if (self->channels[i].state
== SI_STATE_PENDING)
self->channels[i].state
= SI_STATE_DELTA_TIME;
self->channels_looped = 0;
return SI_LOOP;
}
} else if (retval == SI_FINISHED) {
#ifdef DEBUG
fprintf(stderr, "FINISHED some channel\n");
#endif
} else if (retval > 0) {
int sd ;
sd = _sci1_get_smallest_delta(self);
if (_sci1_no_delta_time(self) && sd) {
/* No other channel is ready */
_sci1_update_delta(self, sd);
/* Only from here do we return delta times */
return sd;
}
}
} /* Not a PCM */
} while (retval > 0); /* All delays must be processed separately */
return retval;
}
static struct _song_iterator *
_sci1_handle_message(sci1_song_iterator_t *self,
song_iterator_message_t msg) {
if (msg.recipient == _SIMSG_BASE) { /* May extend this in the future */
switch (msg.type) {
case _SIMSG_BASEMSG_PRINT: {
int playmask = 0;
int i;
for (i = 0; i < self->channels_nr; i++)
playmask |= self->channels[i].playmask;
print_tabs_id(msg.args[0].i, self->ID);
fprintf(stderr, "SCI1: chan-nr=%d, playmask=%04x\n",
self->channels_nr, playmask);
}
break;
case _SIMSG_BASEMSG_CLONE: {
int tsize = sizeof(sci1_song_iterator_t);
sci1_song_iterator_t *mem = (sci1_song_iterator_t*)sci_malloc(tsize);
sci1_sample_t **samplep;
int delta = msg.args[0].i; /* Delay until next step */
memcpy(mem, self, tsize);
samplep = &(mem->next_sample);
sci_refcount_incref(mem->data);
mem->delay_remaining += delta;
/* Clone chain of samples */
while (*samplep) {
sci1_sample_t *newsample
= (sci1_sample_t*)sci_malloc(sizeof(sci1_sample_t));
memcpy(newsample, *samplep,
sizeof(sci1_sample_t));
*samplep = newsample;
samplep = &(newsample->next);
}
return (struct _song_iterator *) mem; /* Assume caller has another copy of this */
}
case _SIMSG_BASEMSG_STOP: {
songit_id_t sought_id = msg.ID;
int i;
if (sought_id == self->ID) {
self->ID = 0;
for (i = 0; i < self->channels_nr; i++)
self->channels[i].state = SI_STATE_FINISHED;
}
break;
}
case _SIMSG_BASEMSG_SET_PLAYMASK:
if (msg.ID == self->ID) {
self->channel_mask = 0;
self->device_id
= sci0_to_sci1_device_map
[sci_ffs(msg.args[0].i & 0xff) - 1]
[sfx_pcm_available()]
;
if (self->device_id == 0xff) {
sciprintf("[iterator-1] Warning: Device %d(%d) not supported",
msg.args[0].i & 0xff, sfx_pcm_available());
}
if (self->initialised) {
int i;
int toffset = -1;
for (i = 0; i < self->channels_nr; i++)
if (self->channels[i].state != SI_STATE_FINISHED
&& self->channels[i].total_timepos > toffset) {
toffset = self->channels[i].total_timepos
+ self->channels[i].timepos_increment
- self->channels[i].delay;
}
/* Find an active channel so that we can
** get the correct time offset */
_sci1_song_init(self);
toffset -= self->delay_remaining;
self->delay_remaining = 0;
if (toffset > 0)
return new_fast_forward_iterator((song_iterator_t *) self,
toffset);
} else {
_sci1_song_init(self);
self->initialised = 1;
}
break;
}
case _SIMSG_BASEMSG_SET_LOOPS:
if (msg.ID == self->ID)
self->loops = (msg.args[0].i > 32767) ? 99 : 0;
/* 99 is arbitrary, but we can't use '1' because of
** the way we're testing in the decoding section. */
break;
case _SIMSG_BASEMSG_SET_HOLD:
self->hold = msg.args[0].i;
break;
case _SIMSG_BASEMSG_SET_RHYTHM:
/* Ignore */
break;
case _SIMSG_BASEMSG_SET_FADE: {
fade_params_t *fp = (fade_params_t *) msg.args[0].p;
self->fade.action = fp->action;
self->fade.final_volume = fp->final_volume;
self->fade.ticks_per_step = fp->ticks_per_step;
self->fade.step_size = fp->step_size;
break;
}
default:
fprintf(stderr, SIPFX "Unsupported command %d to"
" SCI1 iterator", msg.type);
}
return (song_iterator_t *) self;
}
return NULL;
}
static int
_sci1_read_next_command(sci1_song_iterator_t *self,
unsigned char *buf, int *result) {
return _sci1_process_next_command(self, buf, result);
}
static void
_sci1_init(sci1_song_iterator_t *self) {
_common_init((base_song_iterator_t *) self);
self->ccc = 127;
self->device_id = 0x00; /* Default to Sound Blaster/Adlib for purposes
** of cue computation */
self->next_sample = NULL;
self->channels_nr = 0;
self->initialised = 0;
self->delay_remaining = 0;
self->loops = 0;
self->hold = 0;
memset(self->polyphony, 0, sizeof(self->polyphony));
memset(self->importance, 0, sizeof(self->importance));
}
static void
_sci1_cleanup(sci1_song_iterator_t *it) {
sci1_sample_t *sample_seeker = it->next_sample;
while (sample_seeker) {
sci1_sample_t *old_sample = sample_seeker;
sample_seeker = sample_seeker->next;
sci_free(old_sample);
}
_sci0_cleanup((sci0_song_iterator_t *)it);
}
static int
_sci1_get_timepos(sci1_song_iterator_t *self) {
int max = 0;
int i;
for (i = 0; i < self->channels_nr; i++)
if (self->channels[i].total_timepos > max)
max = self->channels[i].total_timepos;
return max;
}
/*****************************/
/*-- Cleanup song iterator --*/
/*****************************/
static void
_cleanup_iterator_init(song_iterator_t *it) {
}
static song_iterator_t *
_cleanup_iterator_handle_message(song_iterator_t *i, song_iterator_message_t msg) {
if (msg.recipient == _SIMSG_BASEMSG_PRINT
&& msg.type == _SIMSG_BASEMSG_PRINT) {
print_tabs_id(msg.args[0].i, i->ID);
fprintf(stderr, "CLEANUP\n");
}
return NULL;
}
static int
_cleanup_iterator_next(song_iterator_t *self, unsigned char *buf, int *result) {
/* Task: Return channel-notes-off for each channel */
if (self->channel_mask) {
int bs = sci_ffs(self->channel_mask) - 1;
self->channel_mask &= ~(1 << bs);
buf[0] = 0xb0 | bs; /* Controller */
buf[1] = SCI_MIDI_CHANNEL_NOTES_OFF;
buf[2] = 0; /* Hmm... */
*result = 3;
return 0;
} else
return SI_FINISHED;
}
song_iterator_t *
new_cleanup_iterator(unsigned int channels) {
song_iterator_t *it = (song_iterator_t*)sci_malloc(sizeof(song_iterator_t));
it->channel_mask = channels;
it->ID = 17;
it->flags = 0;
it->death_listeners_nr = 0;
it->cleanup = NULL;
it->get_pcm_feed = NULL;
it->init = _cleanup_iterator_init;
it->handle_message = _cleanup_iterator_handle_message;
it->get_timepos = NULL;
it->next = _cleanup_iterator_next;
return it;
}
/**********************************/
/*-- Fast-forward song iterator --*/
/**********************************/
static int
_ff_read_next_command(fast_forward_song_iterator_t *self,
byte *buf, int *result) {
int rv;
if (self->delta <= 0)
return SI_MORPH; /* Did our duty */
while (1) {
rv = self->delegate->next(self->delegate, buf, result);
if (rv > 0) {
/* Subtract from the delta we want to wait */
self->delta -= rv;
/* Done */
if (self->delta < 0)
return -self->delta;
}
if (rv <= 0)
return rv;
}
}
static sfx_pcm_feed_t *
_ff_check_pcm(fast_forward_song_iterator_t *self) {
return self->delegate->get_pcm_feed(self->delegate);
}
static song_iterator_t *
_ff_handle_message(fast_forward_song_iterator_t *self,
song_iterator_message_t msg) {
if (msg.recipient == _SIMSG_PLASTICWRAP)
switch (msg.type) {
case _SIMSG_PLASTICWRAP_ACK_MORPH:
if (self->delta <= 0) {
song_iterator_t *it = self->delegate;
sci_free(self);
return it;
}
break;
default:
BREAKPOINT();
}
else if (msg.recipient == _SIMSG_BASE) {
switch (msg.type) {
case _SIMSG_BASEMSG_CLONE: {
int tsize = sizeof(fast_forward_song_iterator_t);
fast_forward_song_iterator_t *clone = (fast_forward_song_iterator_t *)sci_malloc(tsize);
memcpy(clone, self, tsize);
songit_handle_message(&clone->delegate, msg);
return (song_iterator_t *) clone;
}
case _SIMSG_BASEMSG_PRINT:
print_tabs_id(msg.args[0].i, self->ID);
fprintf(stderr, "PLASTICWRAP:\n");
msg.args[0].i++;
songit_handle_message(&(self->delegate), msg);
break;
default:
songit_handle_message(&(self->delegate), msg);
}
} else
songit_handle_message(&(self->delegate), msg);
return NULL;
}
static void
_ff_init(fast_forward_song_iterator_t *self) {
return;
}
static int
_ff_get_timepos(fast_forward_song_iterator_t *self) {
return self->delegate->get_timepos(self->delegate);
}
song_iterator_t *
new_fast_forward_iterator(song_iterator_t *capsit, int delta) {
fast_forward_song_iterator_t *it =
(fast_forward_song_iterator_t*)sci_malloc(sizeof(fast_forward_song_iterator_t));
if (capsit == NULL) {
free(it);
return NULL;
}
it->ID = 0;
it->delegate = capsit;
it->delta = delta;
it->death_listeners_nr = 0;
it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
_ff_read_next_command;
it->get_pcm_feed = (sfx_pcm_feed_t * (*)(song_iterator_t *))
_ff_check_pcm;
it->handle_message = (song_iterator_t * (*)(song_iterator_t *,
song_iterator_message_t))
_ff_handle_message;
it->get_timepos = (int(*)(song_iterator_t *))_ff_get_timepos;
it->init = (void(*)(song_iterator_t *))
_ff_init;
it->cleanup = NULL;
it->channel_mask = capsit->channel_mask;
return (song_iterator_t *) it;
}
/********************/
/*-- Tee iterator --*/
/********************/
static int
_tee_read_next_command(tee_song_iterator_t *it, unsigned char *buf,
int *result) {
static int ready_masks[2] = {TEE_LEFT_READY, TEE_RIGHT_READY};
static int active_masks[2] = {TEE_LEFT_ACTIVE, TEE_RIGHT_ACTIVE};
static int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM};
int i;
int retid;
#ifdef DEBUG_TEE_ITERATOR
fprintf(stderr, "[Tee] %02x\n", it->status);
#endif
if (!(it->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)))
/* None is active? */
return SI_FINISHED;
if (it->morph_deferred == TEE_MORPH_READY)
return SI_MORPH;
if ((it->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))
!= (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) {
/* Not all are is active? */
int which = 0;
#ifdef DEBUG_TEE_ITERATOR
fprintf(stderr, "\tRequesting transformation...\n");
#endif
if (it->status & TEE_LEFT_ACTIVE)
which = TEE_LEFT;
else if (it->status & TEE_RIGHT_ACTIVE)
which = TEE_RIGHT;
memcpy(buf, it->children[which].buf, MAX_BUF_SIZE);
*result = it->children[which].result;
it->morph_deferred = TEE_MORPH_READY;
return it->children[which].retval;
}
/* First, check for unreported PCMs */
for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
if ((it->status & (ready_masks[i] | pcm_masks[i]))
== (ready_masks[i] | pcm_masks[i])) {
it->status &= ~ready_masks[i];
return SI_PCM;
}
for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
if (!(it->status & ready_masks[i])) {
/* Buffers aren't ready yet */
it->children[i].retval =
songit_next(&(it->children[i].it),
it->children[i].buf,
&(it->children[i].result),
IT_READER_MASK_ALL
| IT_READER_MAY_FREE
| IT_READER_MAY_CLEAN);
it->status |= ready_masks[i];
#ifdef DEBUG_TEE_ITERATOR
fprintf(stderr, "\t Must check %d: %d\n", i,
it->children[i].retval);
#endif
if (it->children[i].retval == SI_ABSOLUTE_CUE ||
it->children[i].retval == SI_RELATIVE_CUE)
return it->children[i].retval;
if (it->children[i].retval == SI_FINISHED) {
it->status &= ~active_masks[i];
/* Recurse to complete */
#ifdef DEBUG_TEE_ITERATOR
fprintf(stderr, "\t Child %d signalled completion, recursing w/ status %02x\n", i, it->status);
#endif
return _tee_read_next_command(it, buf, result);
} else if (it->children[i].retval == SI_PCM) {
it->status |= pcm_masks[i];
it->status &= ~ready_masks[i];
return SI_PCM;
}
}
/* We've already handled PCM, MORPH and FINISHED, CUEs & LOOP remain */
retid = TEE_LEFT;
if ((it->children[TEE_LEFT].retval > 0)
/* Asked to delay */
&& (it->children[TEE_RIGHT].retval <= it->children[TEE_LEFT].retval))
/* Is not delaying or not delaying as much */
retid = TEE_RIGHT;
#ifdef DEBUG_TEE_ITERATOR
fprintf(stderr, "\tl:%d / r:%d / chose %d\n",
it->children[TEE_LEFT].retval, it->children[TEE_RIGHT].retval, retid);
#endif
#if 0
if (it->children[retid].retval == 0) {
/* Perform remapping, if neccessary */
byte *buf = it->children[retid].buf;
if (*buf != SCI_MIDI_SET_SIGNAL
&& *buf < 0xf0) { /* Not a generic command */
int chan = *buf & 0xf;
int op = *buf & 0xf0;
chan = it->children[retid].channel_remap[chan];
*buf = chan | op;
}
}
#endif
/* Adjust delta times */
if (it->children[retid].retval > 0
&& it->children[1-retid].retval > 0) {
if (it->children[1-retid].retval
== it->children[retid].retval)
/* If both children wait the same amount of time,
** we have to re-fetch commands from both */
it->status &= ~ready_masks[1-retid];
else
/* If they don't, we can/must re-use the other
** child's delay time */
it->children[1-retid].retval
-= it->children[retid].retval;
}
it->status &= ~ready_masks[retid];
memcpy(buf, it->children[retid].buf, MAX_BUF_SIZE);
*result = it->children[retid].result;
return it->children[retid].retval;
}
static sfx_pcm_feed_t *
_tee_check_pcm(tee_song_iterator_t *it) {
static int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM};
int i;
for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
if (it->status & pcm_masks[i]) {
it->status &= ~pcm_masks[i];
return it->children[i].it->
get_pcm_feed(it->children[i].it);
}
return NULL; /* No iterator */
}
static song_iterator_t *
_tee_handle_message(tee_song_iterator_t *self, song_iterator_message_t msg) {
if (msg.recipient == _SIMSG_BASE) {
switch (msg.type) {
case _SIMSG_BASEMSG_PRINT:
print_tabs_id(msg.args[0].i, self->ID);
fprintf(stderr, "TEE:\n");
msg.args[0].i++;
break; /* And continue with our children */
case _SIMSG_BASEMSG_CLONE: {
tee_song_iterator_t *newit
= (tee_song_iterator_t*)sci_malloc(sizeof(tee_song_iterator_t));
memcpy(newit, self, sizeof(tee_song_iterator_t));
if (newit->children[TEE_LEFT].it)
newit->children[TEE_LEFT].it =
songit_clone(newit->children[TEE_LEFT].it, msg.args[0].i);
if (newit->children[TEE_RIGHT].it)
newit->children[TEE_RIGHT].it =
songit_clone(newit->children[TEE_RIGHT].it, msg.args[0].i);
return (song_iterator_t *) newit;
}
default:
break;
}
}
if (msg.recipient == _SIMSG_PLASTICWRAP) {
song_iterator_t *old_it;
switch (msg.type) {
case _SIMSG_PLASTICWRAP_ACK_MORPH:
if (!(self->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) {
songit_free((song_iterator_t *) self);
return NULL;
} else if (!(self->status & TEE_LEFT_ACTIVE)) {
if (self->may_destroy)
songit_free(self->children[TEE_LEFT].it);
old_it = self->children[TEE_RIGHT].it;
sci_free(self);
return old_it;
} else if (!(self->status & TEE_RIGHT_ACTIVE)) {
if (self->may_destroy)
songit_free(self->children[TEE_RIGHT].it);
old_it = self->children[TEE_LEFT].it;
sci_free(self);
return old_it;
} else {
sciprintf("[tee-iterator] WARNING:"
" Morphing without need\n");
return (song_iterator_t *) self;
}
default:
BREAKPOINT();
}
}
if (self->children[TEE_LEFT].it)
songit_handle_message(&(self->children[TEE_LEFT].it), msg);
if (self->children[TEE_RIGHT].it)
songit_handle_message(&(self->children[TEE_RIGHT].it), msg);
return NULL;
}
static void
_tee_init(tee_song_iterator_t *it) {
it->status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE;
it->children[TEE_LEFT].it->init(it->children[TEE_LEFT].it);
it->children[TEE_RIGHT].it->init(it->children[TEE_RIGHT].it);
}
#if 0
// Unreferenced - removed
static void
_tee_free(tee_song_iterator_t *it) {
int i;
for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
if (it->children[i].it && it->may_destroy)
songit_free(it->children[i].it);
}
#endif
static void
songit_tee_death_notification(tee_song_iterator_t *self,
song_iterator_t *corpse) {
if (corpse == self->children[TEE_LEFT].it) {
self->status &= ~TEE_LEFT_ACTIVE;
self->children[TEE_LEFT].it = NULL;
} else if (corpse == self->children[TEE_RIGHT].it) {
self->status &= ~TEE_RIGHT_ACTIVE;
self->children[TEE_RIGHT].it = NULL;
} else {
BREAKPOINT();
}
}
song_iterator_t *
songit_new_tee(song_iterator_t *left, song_iterator_t *right, int may_destroy) {
int i;
int firstfree = 1; /* First free channel */
int incomplete_map = 0;
tee_song_iterator_t *it = (tee_song_iterator_t*)sci_malloc(sizeof(tee_song_iterator_t));
it->ID = 0;
it->morph_deferred = TEE_MORPH_NONE;
it->status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE;
it->may_destroy = may_destroy;
it->children[TEE_LEFT].it = left;
it->children[TEE_RIGHT].it = right;
it->death_listeners_nr = 0;
/* By default, don't remap */
for (i = 0; i < 16; i++)
it->children[TEE_LEFT].channel_remap[i]
= it->children[TEE_RIGHT].channel_remap[i] = i;
/* Default to lhs channels */
it->channel_mask = left->channel_mask;
for (i = 0; i < 16; i++)
if (it->channel_mask & (1 << i) & right->channel_mask
&& (i != MIDI_RHYTHM_CHANNEL) /* Share rhythm */) { /*conflict*/
while ((firstfree == MIDI_RHYTHM_CHANNEL)
/* Either if it's the rhythm channel or if it's taken */
|| (firstfree < MIDI_CHANNELS
&& ((1 << firstfree) & it->channel_mask)))
++firstfree;
if (firstfree == MIDI_CHANNELS) {
incomplete_map = 1;
fprintf(stderr, "[songit-tee <%08lx,%08lx>] "
"Could not remap right channel #%d:"
" Out of channels\n",
left->ID, right->ID, i);
} else {
it->children[TEE_RIGHT].channel_remap[i]
= firstfree;
it->channel_mask |= (1 << firstfree);
}
}
#ifdef DEBUG_TEE_ITERATOR
if (incomplete_map) {
int c;
fprintf(stderr, "[songit-tee <%08lx,%08lx>] Channels:"
" %04x <- %04x | %04x\n",
left->ID, right->ID,
it->channel_mask,
left->channel_mask, right->channel_mask);
for (c = 0 ; c < 2; c++)
for (i = 0 ; i < 16; i++)
fprintf(stderr, " map [%d][%d] -> %d\n",
c, i, it->children[c].channel_remap[i]);
}
#endif
it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
_tee_read_next_command;
it->get_pcm_feed = (sfx_pcm_feed_t * (*)(song_iterator_t *))
_tee_check_pcm;
it->handle_message = (song_iterator_t * (*)(song_iterator_t *,
song_iterator_message_t))
_tee_handle_message;
it->init = (void(*)(song_iterator_t *))
_tee_init;
it->get_timepos = NULL;
song_iterator_add_death_listener((song_iterator_t *)it,
left, (void (*)(void *, void*))
songit_tee_death_notification);
song_iterator_add_death_listener((song_iterator_t *)it,
right, (void (*)(void *, void*))
songit_tee_death_notification);
it->cleanup = NULL;
return (song_iterator_t *) it;
}
/*************************************/
/*-- General purpose functionality --*/
/*************************************/
int
songit_next(song_iterator_t **it, unsigned char *buf, int *result, int mask) {
int retval;
if (!*it)
return SI_FINISHED;
do {
retval = (*it)->next(*it, buf, result);
if (retval == SI_MORPH) {
fprintf(stderr, " Morphing %p (stored at %p)\n", (void *)*it, (void *)it);
if (!SIMSG_SEND((*it), SIMSG_ACK_MORPH)) {
BREAKPOINT();
} else fprintf(stderr, "SI_MORPH successful\n");
}
if (retval == SI_FINISHED)
fprintf(stderr, "[song-iterator] Song finished. mask = %04x, cm=%04x\n",
mask, (*it)->channel_mask);
if (retval == SI_FINISHED
&& (mask & IT_READER_MAY_CLEAN)
&& (*it)->channel_mask) { /* This last test will fail
** with a terminated
** cleanup iterator */
int channel_mask = (*it)->channel_mask;
if (mask & IT_READER_MAY_FREE)
songit_free(*it);
*it = new_cleanup_iterator(channel_mask);
retval = -9999; /* Continue */
}
} while (!( /* Until one of the following holds */
(retval > 0 && (mask & IT_READER_MASK_DELAY))
|| (retval == 0 && (mask & IT_READER_MASK_MIDI))
|| (retval == SI_LOOP && (mask & IT_READER_MASK_LOOP))
|| (retval == SI_ABSOLUTE_CUE &&
(mask & IT_READER_MASK_CUE))
|| (retval == SI_RELATIVE_CUE &&
(mask & IT_READER_MASK_CUE))
|| (retval == SI_PCM && (mask & IT_READER_MASK_PCM))
|| (retval == SI_FINISHED)
));
if (retval == SI_FINISHED
&& (mask & IT_READER_MAY_FREE)) {
songit_free(*it);
*it = NULL;
}
return retval;
}
song_iterator_t *
songit_new(unsigned char *data, unsigned int size, int type, songit_id_t id) {
base_song_iterator_t *it;
int i;
if (!data || size < 22) {
fprintf(stderr, SIPFX "Attempt to instantiate song iterator for null"
" song data\n");
return NULL;
}
switch (type) {
case SCI_SONG_ITERATOR_TYPE_SCI0:
/**-- Playing SCI0 sound resources --**/
it = (base_song_iterator_t*)sci_malloc(sizeof(sci0_song_iterator_t));
it->channel_mask = 0xffff; /* Allocate all channels by default */
for (i = 0; i < MIDI_CHANNELS; i++)
it->polyphony[i] = data[1 + (i << 1)];
it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
_sci0_read_next_command;
it->get_pcm_feed = (sfx_pcm_feed_t * (*)(song_iterator_t *))
_sci0_check_pcm;
it->handle_message = (song_iterator_t * (*)(song_iterator_t *, song_iterator_message_t))
_sci0_handle_message;
it->init = (void(*)(song_iterator_t *))_sci0_init;
it->cleanup = (void(*)(song_iterator_t *))_sci0_cleanup;
((sci0_song_iterator_t *)it)->channel.state
= SI_STATE_UNINITIALISED;
it->get_timepos = (int(*)(song_iterator_t *))_sci0_get_timepos;
break;
case SCI_SONG_ITERATOR_TYPE_SCI1:
/**-- SCI01 or later sound resource --**/
it = (base_song_iterator_t*)sci_malloc(sizeof(sci1_song_iterator_t));
it->channel_mask = 0; /* Defer channel allocation */
for (i = 0; i < MIDI_CHANNELS; i++)
it->polyphony[i] = 0; /* Unknown */
it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
_sci1_read_next_command;
it->get_pcm_feed = (sfx_pcm_feed_t * (*)(song_iterator_t *))
_sci1_get_pcm;
it->handle_message = (song_iterator_t * (*)(song_iterator_t *, song_iterator_message_t))
_sci1_handle_message;
it->init = (void(*)(song_iterator_t *))_sci1_init;
it->cleanup = (void(*)(song_iterator_t *))_sci1_cleanup;
it->get_timepos = (int(*)(song_iterator_t *))_sci1_get_timepos;
break;
default:
/**-- Invalid/unsupported sound resources --**/
fprintf(stderr, SIPFX "Attempt to instantiate invalid/unknown"
" song iterator type %d\n", type);
return NULL;
}
it->ID = id;
it->death_listeners_nr = 0;
it->data = (unsigned char*)sci_refcount_memdup(data, size);
it->size = size;
it->init((song_iterator_t *) it);
return (song_iterator_t *) it;
}
void
songit_free(song_iterator_t *it) {
if (it) {
int i;
if (it->cleanup)
it->cleanup(it);
for (i = 0; i < it->death_listeners_nr; i++)
it->death_listeners[i].notify(it->death_listeners[i].self, it);
sci_free(it);
}
}
song_iterator_message_t
songit_make_message(songit_id_t id, int recipient, int type, int a1, int a2) {
song_iterator_message_t rv;
rv.ID = id;
rv.recipient = recipient;
rv.type = type;
rv.args[0].i = a1;
rv.args[1].i = a2;
return rv;
}
song_iterator_message_t
songit_make_ptr_message(songit_id_t id, int recipient, int type, void * a1, int a2) {
song_iterator_message_t rv;
rv.ID = id;
rv.recipient = recipient;
rv.type = type;
rv.args[0].p = a1;
rv.args[1].i = a2;
return rv;
}
int
songit_handle_message(song_iterator_t **it_reg_p, song_iterator_message_t msg) {
song_iterator_t *it = *it_reg_p;
song_iterator_t *newit;
newit = it->handle_message(it, msg);
if (!newit)
return 0; /* Couldn't handle */
*it_reg_p = newit; /* Might have self-morphed */
return 1;
}
song_iterator_t *
songit_clone(song_iterator_t *it, int delta) {
SIMSG_SEND(it, SIMSG_CLONE(delta));
it->death_listeners_nr = 0;
it->flags |= SONGIT_FLAG_CLONE;
return it;
}
void
song_iterator_add_death_listener(song_iterator_t *it,
void *client,
void (*notify)(void *self, void *notifier)) {
if (it->death_listeners_nr >= SONGIT_MAX_LISTENERS) {
fprintf(stderr, "FATAL: Too many death listeners for song"
" iterator\n");
BREAKPOINT();
exit(1);
}
it->death_listeners[it->death_listeners_nr].notify = notify;
it->death_listeners[it->death_listeners_nr].self = client;
it->death_listeners_nr++;
}
void
song_iterator_remove_death_listener(song_iterator_t *it,
void *client) {
int i;
for (i = 0; i < it->death_listeners_nr; i++) {
if (it->death_listeners[i].self == client) {
--it->death_listeners_nr;
/* Overwrite, if this wasn't the last one */
if (i + 1 < it->death_listeners_nr)
it->death_listeners[i]
= it->death_listeners[it->death_listeners_nr];
return;
}
}
fprintf(stderr, "FATAL: Could not remove death listener from "
"song iterator\n");
BREAKPOINT();
exit(1);
}
song_iterator_t *
sfx_iterator_combine(song_iterator_t *it1, song_iterator_t *it2) {
if (it1 == NULL)
return it2;
if (it2 == NULL)
return it1;
/* Both are non-NULL: */
return songit_new_tee(it1, it2, 1); /* 'may destroy' */
}