/*************************************************************************** Copyright (C) 2008 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 merchantability, 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) ***************************************************************************/ #include #include "sci_midi.h" #include "sci_memory.h" #include "instrument-map.h" #include "sfx_engine.h" sfx_instrument_map_t * sfx_instrument_map_new(int velocity_maps_nr) { sfx_instrument_map_t *map = (sfx_instrument_map_t *)sci_malloc(sizeof(sfx_instrument_map_t)); int i; map->initialisation_block_size = 0; map->initialisation_block = NULL; map->velocity_maps_nr = velocity_maps_nr; map->percussion_velocity_map_index = SFX_NO_VELOCITY_MAP; if (velocity_maps_nr == 0) map->velocity_map = NULL; /* Yes, this complicates control flow needlessly, but it avoids some of the pointless ** warnings that certain memory tools seem to find appropriate. */ else { map->velocity_map = (byte **)sci_malloc(sizeof(byte *) * velocity_maps_nr); for (i = 0; i < velocity_maps_nr; ++i) map->velocity_map[i] = (byte *)sci_malloc(SFX_VELOCITIES_NR); } for (i = 0; i < SFX_INSTRUMENTS_NR; ++i) map->velocity_map_index[i] = SFX_NO_VELOCITY_MAP; map->percussion_volume_adjust = 0; for (i = 0; i < SFX_RHYTHM_NR; ++i) map->percussion_map[i] = i; for (i = 0; i < SFX_INSTRUMENTS_NR; ++i) { map->patch_map[i].patch = i; map->patch_key_shift[i] = 0; map->patch_volume_adjust[i] = 0; } return map; } void sfx_instrument_map_free(sfx_instrument_map_t *map) { if (!map) return; if (map->velocity_map) { int i; for (i = 0; i < map->velocity_maps_nr; i++) sci_free(map->velocity_map[i]); sci_free(map->velocity_map); map->velocity_map = NULL; } if (map->initialisation_block) { sci_free(map->initialisation_block); map->initialisation_block = NULL; } sci_free(map); } #define PATCH_MAP_OFFSET 0x0000 #define PATCH_KEY_SHIFT_OFFSET 0x0080 #define PATCH_VOLUME_ADJUST_OFFSET 0x0100 #define PATCH_PERCUSSION_MAP_OFFSET 0x0180 #define PATCH_PERCUSSION_VOLUME_ADJUST 0x0200 #define PATCH_VELOCITY_MAP_INDEX 0x0201 #define PATCH_VELOCITY_MAP(i) (0x0281 + (0x80 * i)) #define PATCH_INIT_DATA_SIZE_LE 0x0481 #define PATCH_INIT_DATA 0x0483 #define PATCH_INSTRUMENT_MAPS_NR 4 #define PATCH_MIN_SIZE PATCH_INIT_DATA static int patch001_type0_length(byte *data, size_t length) { unsigned int pos = 492 + 246 * data[491]; /* printf("timbres %d (post = %04x)\n",data[491], pos);*/ if ((length >= (pos + 386)) && (data[pos] == 0xAB) && (data[pos + 1] == 0xCD)) pos += 386; /* printf("pos = %04x (%02x %02x)\n", pos, data[pos], data[pos + 1]); */ if ((length >= (pos + 267)) && (data[pos] == 0xDC) && (data[pos + 1] == 0xBA)) pos += 267; /* printf("pos = %04x %04x (%d)\n", pos, length, pos-length); */ if (pos == length) return 1; return 0; } static int patch001_type1_length(byte *data, size_t length) { if ((length >= 1155) && (((data[1154] << 8) + data[1153] + 1155) == length)) return 1; return 0; } int sfx_instrument_map_detect(byte *data, size_t length) { /* length test */ if (length < 1155) return SFX_MAP_MT32; if (length > 16889) return SFX_MAP_MT32_GM; if (patch001_type0_length(data, length) && !patch001_type1_length(data, length)) return SFX_MAP_MT32; if (patch001_type1_length(data, length) && !patch001_type0_length(data, length)) return SFX_MAP_MT32_GM; return SFX_MAP_UNKNOWN; } sfx_instrument_map_t * sfx_instrument_map_load_sci(byte *data, size_t size) { sfx_instrument_map_t * map; int i, m; if (data == NULL) return NULL; if (size < PATCH_MIN_SIZE) { fprintf(stderr, "[instrument-map] Instrument map too small: %d of %d\n", (int) size, PATCH_MIN_SIZE); return NULL; } map = sfx_instrument_map_new(PATCH_INSTRUMENT_MAPS_NR); /* Set up MIDI intialisation data */ map->initialisation_block_size = getInt16(data + PATCH_INIT_DATA_SIZE_LE); if (map->initialisation_block_size) { if (size < PATCH_MIN_SIZE + map->initialisation_block_size) { fprintf(stderr, "[instrument-map] Instrument map too small for initialisation block: %d of %d\n", (int) size, PATCH_MIN_SIZE); return NULL; } if (size > PATCH_MIN_SIZE + map->initialisation_block_size) fprintf(stderr, "[instrument-map] Instrument larger than required by initialisation block: %d of %d\n", (int) size, PATCH_MIN_SIZE); if (map->initialisation_block_size != 0) { map->initialisation_block = (byte *)sci_malloc(map->initialisation_block_size); memcpy(map->initialisation_block, data + PATCH_INIT_DATA, map->initialisation_block_size); } } /* Set up basic instrument info */ for (i = 0; i < SFX_INSTRUMENTS_NR; i++) { map->patch_map[i].patch = (char)data[PATCH_MAP_OFFSET + i]; map->patch_key_shift[i] = (char)data[PATCH_KEY_SHIFT_OFFSET + i]; map->patch_volume_adjust[i] = (char)data[PATCH_VOLUME_ADJUST_OFFSET + i]; map->patch_bend_range[i] = SFX_UNMAPPED; map->velocity_map_index[i] = data[PATCH_VELOCITY_MAP_INDEX + i]; } /* Set up percussion maps */ map->percussion_volume_adjust = data[PATCH_PERCUSSION_VOLUME_ADJUST]; for (i = 0; i < SFX_RHYTHM_NR; i++) { map->percussion_map[i] = data[PATCH_PERCUSSION_MAP_OFFSET + i]; map->percussion_velocity_scale[i] = SFX_MAX_VELOCITY; } /* Set up velocity maps */ for (m = 0; m < PATCH_INSTRUMENT_MAPS_NR; m++) { byte *velocity_map = map->velocity_map[m]; for (i = 0; i < SFX_VELOCITIES_NR; i++) velocity_map[i] = data[PATCH_VELOCITY_MAP(m) + i]; } map->percussion_velocity_map_index = 0; return map; } /* Output with the instrument map */ #define MIDI_CHANNELS_NR 0x10 // FIXME: Replace this ugly hack with simple subclassing once converting to C++ typedef struct decorated_midi_writer { MIDI_WRITER_BODY midi_writer_t *writer; sfx_patch_map_t patches[MIDI_CHANNELS_NR]; sfx_instrument_map_t *map; } decorated_midi_writer_t; static void init_decorated(struct _midi_writer *self_) { decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; self->writer->init(self->writer); } static void set_option_decorated(struct _midi_writer *self_, char *name, char *value) { decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; self->writer->set_option(self->writer, name, value); } static void delay_decorated(struct _midi_writer *self_, int ticks) { decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; self->writer->delay(self->writer, ticks); } static void flush_decorated(struct _midi_writer *self_) { decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; if (self->writer->flush) self->writer->flush(self->writer); } static void reset_timer_decorated(struct _midi_writer *self_) { decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; self->writer->reset_timer(self->writer); } static void close_decorated(decorated_midi_writer_t *self) { sfx_instrument_map_free(self->map); self->map = NULL; self->writer->close(self->writer); sci_free((void *)self->name); self->name = NULL; sci_free(self); } #define BOUND_127(x) (((x) < 0)? 0 : (((x) > 0x7f)? 0x7f : (x))) static int bound_hard_127(int i, const char *descr) { int r = BOUND_127(i); if (r != i) fprintf(stderr, "[instrument-map] Hard-clipping %02x to %02x in %s\n", i, r, descr); return r; } static int set_bend_range(midi_writer_t *writer, int channel, int range) { byte buf[3] = {0xb0, 0x65, 0x00}; buf[0] |= channel & 0xf; if (writer->write(writer, buf, 3) != SFX_OK) return SFX_ERROR; buf[1] = 0x64; if (writer->write(writer, buf, 3) != SFX_OK) return SFX_ERROR; buf[1] = 0x06; buf[2] = BOUND_127(range); if (writer->write(writer, buf, 3) != SFX_OK) return SFX_ERROR; buf[1] = 0x26; buf[2] = 0; if (writer->write(writer, buf, 3) != SFX_OK) return SFX_ERROR; return SFX_OK; } static int write_decorated(decorated_midi_writer_t *self, byte *buf, int len) { sfx_instrument_map_t *map = self->map; int op = *buf & 0xf0; int chan = *buf & 0x0f; int patch = self->patches[chan].patch; int rhythm = self->patches[chan].rhythm; assert(len >= 1); if (op == 0xC0 && chan != MIDI_RHYTHM_CHANNEL) { /* Program change */ /*int*/ patch = bound_hard_127(buf[1], "program change"); int instrument = map->patch_map[patch].patch; int bend_range = map->patch_bend_range[patch]; self->patches[chan] = map->patch_map[patch]; if (instrument == SFX_UNMAPPED || instrument == SFX_MAPPED_TO_RHYTHM) return SFX_OK; assert(len >= 2); buf[1] = bound_hard_127(instrument, "patch lookup"); if (self->writer->write(self->writer, buf, len) != SFX_OK) return SFX_ERROR; if (bend_range != SFX_UNMAPPED) return set_bend_range(self->writer, chan, bend_range); return SFX_OK; } if (chan == MIDI_RHYTHM_CHANNEL || patch == SFX_MAPPED_TO_RHYTHM) { /* Rhythm channel handling */ switch (op) { case 0x80: case 0x90: { /* Note off / note on */ int velocity, instrument, velocity_map_index, velocity_scale; if (patch == SFX_MAPPED_TO_RHYTHM) { buf[0] = (buf[0] & ~0x0f) | MIDI_RHYTHM_CHANNEL; instrument = rhythm; velocity_scale = SFX_MAX_VELOCITY; } else { int instrument_index = bound_hard_127(buf[1], "rhythm instrument index"); instrument = map->percussion_map[instrument_index]; velocity_scale = map->percussion_velocity_scale[instrument_index]; } if (instrument == SFX_UNMAPPED) return SFX_OK; assert(len >= 3); velocity = bound_hard_127(buf[2], "rhythm velocity"); velocity_map_index = map->percussion_velocity_map_index; if (velocity_map_index != SFX_NO_VELOCITY_MAP) velocity = BOUND_127(velocity + map->velocity_map[velocity_map_index][velocity]); velocity = BOUND_127(velocity * velocity_scale / SFX_MAX_VELOCITY); buf[1] = bound_hard_127(instrument, "rhythm instrument"); buf[2] = velocity; break; } case 0xB0: { /* Controller change */ assert(len >= 3); if (buf[1] == 0x7) /* Volume change */ buf[2] = BOUND_127(buf[2] + map->percussion_volume_adjust); break; } default: break; } } else { /* Instrument channel handling */ if (patch == SFX_UNMAPPED) return SFX_OK; switch (op) { case 0x80: case 0x90: { /* Note off / note on */ int note = bound_hard_127(buf[1], "note"); int velocity = bound_hard_127(buf[2], "velocity"); int velocity_map_index = map->velocity_map_index[patch]; assert(len >= 3); note += map->patch_key_shift[patch]; /* Not the most efficient solutions, but the least error-prone */ while (note < 0) note += 12; while (note > 0x7f) note -= 12; if (velocity_map_index != SFX_NO_VELOCITY_MAP) velocity = BOUND_127(velocity + map->velocity_map[velocity_map_index][velocity]); buf[1] = note; buf[2] = velocity; break; } case 0xB0: /* Controller change */ assert(len >= 3); if (buf[1] == 0x7) /* Volume change */ buf[2] = BOUND_127(buf[2] + map->patch_volume_adjust[patch]); break; default: break; } } return self->writer->write(self->writer, buf, len); } #define MIDI_BYTES_PER_SECOND 3250 /* This seems to be the minimum guarantee by the standard */ #define MAX_PER_TICK (MIDI_BYTES_PER_SECOND / 60) /* After this, we ought to issue one tick of pause */ static void init(midi_writer_t *writer, byte *data, size_t len) { int offset = 0; byte status = 0; /* Send init data as separate MIDI commands */ while (offset < len) { int args; byte op = data[offset]; byte msg[3]; int i; if (op == 0xf0) { int msg_len; byte *find = (byte *) memchr(data + offset, 0xf7, len - offset); if (!find) { fprintf(stderr, "[instrument-map] Failed to find end of sysex message\n"); return; } msg_len = find - data - offset + 1; writer->write(writer, data + offset, msg_len); /* Wait at least 40ms after sysex */ writer->delay(writer, 3); offset += msg_len; continue; } if (op < 0x80) op = status; else { status = op; offset++; } msg[0] = op; switch (op & 0xf0) { case 0xc0: case 0xd0: args = 1; break; default: args = 2; } if (args > len - offset) { fprintf(stderr, "[instrument-map] Insufficient bytes remaining for MIDI command %02x\n", op); return; } for (i = 0; i < args; i++) msg[i + 1] = data[offset + i]; writer->write(writer, msg, args + 1); offset += args; if (writer->flush) writer->flush(writer); } } #define NAME_SUFFIX "+instruments" midi_writer_t * sfx_mapped_writer(midi_writer_t *writer, sfx_instrument_map_t *map) { int i; decorated_midi_writer_t *retval; if (map == NULL) return writer; retval = (decorated_midi_writer_t *)sci_malloc(sizeof(decorated_midi_writer_t)); retval->writer = writer; retval->name = (char *)sci_malloc(strlen(writer->name) + strlen(NAME_SUFFIX) + 1); strcpy(retval->name, writer->name); strcat(retval->name, NAME_SUFFIX); retval->init = (int (*)(midi_writer_t *)) init_decorated; retval->set_option = (int (*)(midi_writer_t *, char *, char *)) set_option_decorated; retval->write = (int (*)(midi_writer_t *, byte *, int)) write_decorated; retval->delay = (void (*)(midi_writer_t *, int)) delay_decorated; retval->flush = (void (*)(midi_writer_t *)) flush_decorated; retval->reset_timer = (void (*)(midi_writer_t *)) reset_timer_decorated; retval->close = (void (*)(midi_writer_t *)) close_decorated; retval->map = map; init(writer, map->initialisation_block, map->initialisation_block_size); for (i = 0; i < MIDI_CHANNELS_NR; i++) retval->patches[i].patch = SFX_UNMAPPED; return (midi_writer_t *) retval; }