gzdoom/src/timidity/instrum_dls.cpp
Randy Heit f9574a98fd Fix Timidity's DLS instrument loading:
- Envelope data needed to be converted to SF2 values.
- Fine tuning was ignored which made pretty much every instrument off tune.
- Despite this, it still sounds like shit compared to FMOD or Microsoft's
  wavetable synth. There are lots of missing notes and some instruments
  are still off tune. I'm not sure it's worth trying to salvage it. It'd
  probably be better to scrap it, since Timidity is very much oriented
  toward GF1 patches, which it handles perfectly fine.
2016-01-13 17:25:24 -06:00

1237 lines
35 KiB
C++

/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
instrum_dls.c
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "timidity.h"
#include "doomdef.h"
#include "m_swap.h"
#define __Sound_SetError(x)
namespace Timidity
{
/*-------------------------------------------------------------------------*/
/* * * * * * * * * * * * * * * * * load_riff.h * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
struct RIFF_Chunk
{
RIFF_Chunk()
{
memset(this, 0, sizeof(*this));
}
~RIFF_Chunk()
{
// data is not freed here because it may be owned by a parent chunk
if (child != NULL)
{
delete child;
}
if (next != NULL)
{
delete next;
}
}
DWORD magic;
DWORD length;
DWORD subtype;
BYTE *data;
RIFF_Chunk *child;
RIFF_Chunk *next;
};
RIFF_Chunk *LoadRIFF(FILE *src);
void FreeRIFF(RIFF_Chunk *chunk);
void PrintRIFF(RIFF_Chunk *chunk, int level);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
/* * * * * * * * * * * * * * * * * load_riff.c * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
#define RIFF MAKE_ID('R','I','F','F')
#define LIST MAKE_ID('L','I','S','T')
static bool ChunkHasSubType(DWORD magic)
{
return (magic == RIFF || magic == LIST);
}
static int ChunkHasSubChunks(DWORD magic)
{
return (magic == RIFF || magic == LIST);
}
static void LoadSubChunks(RIFF_Chunk *chunk, BYTE *data, DWORD left)
{
BYTE *subchunkData;
DWORD subchunkDataLen;
while ( left > 8 ) {
RIFF_Chunk *child = new RIFF_Chunk;
RIFF_Chunk *next, *prev = NULL;
for ( next = chunk->child; next; next = next->next ) {
prev = next;
}
if ( prev ) {
prev->next = child;
} else {
chunk->child = child;
}
child->magic = *(DWORD *)data;
data += 4;
left -= 4;
child->length = LittleLong(*(DWORD *)data);
data += 4;
left -= 4;
child->data = data;
if ( child->length > left ) {
child->length = left;
}
subchunkData = child->data;
subchunkDataLen = child->length;
if ( ChunkHasSubType(child->magic) && subchunkDataLen >= 4 ) {
child->subtype = *(DWORD *)subchunkData;
subchunkData += 4;
subchunkDataLen -= 4;
}
if ( ChunkHasSubChunks(child->magic) ) {
LoadSubChunks(child, subchunkData, subchunkDataLen);
}
data += child->length + (child->length & 1);
left -= child->length + (child->length & 1);
}
}
RIFF_Chunk *LoadRIFF(FILE *src)
{
RIFF_Chunk *chunk;
BYTE *subchunkData;
DWORD subchunkDataLen;
/* Allocate the chunk structure */
chunk = new RIFF_Chunk;
/* Make sure the file is in RIFF format */
fread(&chunk->magic, 4, 1, src);
fread(&chunk->length, 4, 1, src);
chunk->length = LittleLong(chunk->length);
if ( chunk->magic != RIFF ) {
__Sound_SetError("Not a RIFF file");
delete chunk;
return NULL;
}
chunk->data = (BYTE *)malloc(chunk->length);
if ( chunk->data == NULL ) {
__Sound_SetError(ERR_OUT_OF_MEMORY);
delete chunk;
return NULL;
}
if ( fread(chunk->data, chunk->length, 1, src) != 1 ) {
__Sound_SetError(ERR_IO_ERROR);
FreeRIFF(chunk);
return NULL;
}
subchunkData = chunk->data;
subchunkDataLen = chunk->length;
if ( ChunkHasSubType(chunk->magic) && subchunkDataLen >= 4 ) {
chunk->subtype = *(DWORD *)subchunkData;
subchunkData += 4;
subchunkDataLen -= 4;
}
if ( ChunkHasSubChunks(chunk->magic) ) {
LoadSubChunks(chunk, subchunkData, subchunkDataLen);
}
return chunk;
}
void FreeRIFF(RIFF_Chunk *chunk)
{
free(chunk->data);
delete chunk;
}
void PrintRIFF(RIFF_Chunk *chunk, int level)
{
static char prefix[128];
if ( level == sizeof(prefix)-1 ) {
return;
}
if ( level > 0 ) {
prefix[(level-1)*2] = ' ';
prefix[(level-1)*2+1] = ' ';
}
prefix[level*2] = '\0';
printf("%sChunk: %c%c%c%c (%d bytes)", prefix,
((chunk->magic >> 0) & 0xFF),
((chunk->magic >> 8) & 0xFF),
((chunk->magic >> 16) & 0xFF),
((chunk->magic >> 24) & 0xFF), chunk->length);
if ( chunk->subtype ) {
printf(" subtype: %c%c%c%c",
((chunk->subtype >> 0) & 0xFF),
((chunk->subtype >> 8) & 0xFF),
((chunk->subtype >> 16) & 0xFF),
((chunk->subtype >> 24) & 0xFF));
}
printf("\n");
if ( chunk->child ) {
printf("%s{\n", prefix);
PrintRIFF(chunk->child, level + 1);
printf("%s}\n", prefix);
}
if ( chunk->next ) {
PrintRIFF(chunk->next, level);
}
if ( level > 0 ) {
prefix[(level-1)*2] = '\0';
}
}
#ifdef TEST_MAIN_RIFF
main(int argc, char *argv[])
{
int i;
for ( i = 1; i < argc; ++i ) {
RIFF_Chunk *chunk;
SDL_RWops *src = SDL_RWFromFile(argv[i], "rb");
if ( !src ) {
fprintf(stderr, "Couldn't open %s: %s", argv[i], SDL_GetError());
continue;
}
chunk = LoadRIFF(src);
if ( chunk ) {
PrintRIFF(chunk, 0);
FreeRIFF(chunk);
} else {
fprintf(stderr, "Couldn't load %s: %s\n", argv[i], SDL_GetError());
}
SDL_RWclose(src);
}
}
#endif // TEST_MAIN
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
/* * * * * * * * * * * * * * * * * load_dls.h * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
/* This code is based on the DLS spec version 1.1, available at:
http://www.midi.org/about-midi/dls/dlsspec.shtml
*/
/* Some typedefs so the public dls headers don't need to be modified */
#define FAR
typedef SWORD SHORT;
typedef WORD USHORT;
typedef SDWORD LONG;
typedef DWORD ULONG;
#define mmioFOURCC MAKE_ID
#define DEFINE_GUID(A, B, C, E, F, G, H, I, J, K, L, M)
#include "dls1.h"
#include "dls2.h"
struct WaveFMT
{
WORD wFormatTag;
WORD wChannels;
DWORD dwSamplesPerSec;
DWORD dwAvgBytesPerSec;
WORD wBlockAlign;
WORD wBitsPerSample;
};
struct DLS_Wave
{
WaveFMT *format;
BYTE *data;
DWORD length;
WSMPL *wsmp;
WLOOP *wsmp_loop;
};
struct DLS_Region
{
RGNHEADER *header;
WAVELINK *wlnk;
WSMPL *wsmp;
WLOOP *wsmp_loop;
CONNECTIONLIST *art;
CONNECTION *artList;
};
struct DLS_Instrument
{
const char *name;
INSTHEADER *header;
DLS_Region *regions;
CONNECTIONLIST *art;
CONNECTION *artList;
};
struct DLS_Data
{
RIFF_Chunk *chunk;
DWORD cInstruments;
DLS_Instrument *instruments;
POOLTABLE *ptbl;
POOLCUE *ptblList;
DLS_Wave *waveList;
const char *name;
const char *artist;
const char *copyright;
const char *comments;
};
DLS_Data *LoadDLS(FILE *src);
void FreeDLS(DLS_Data *chunk);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
/* * * * * * * * * * * * * * * * * load_dls.c * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
#define FOURCC_LIST mmioFOURCC('L','I','S','T')
#define FOURCC_FMT mmioFOURCC('f','m','t',' ')
#define FOURCC_DATA mmioFOURCC('d','a','t','a')
#define FOURCC_INFO mmioFOURCC('I','N','F','O')
#define FOURCC_IARL mmioFOURCC('I','A','R','L')
#define FOURCC_IART mmioFOURCC('I','A','R','T')
#define FOURCC_ICMS mmioFOURCC('I','C','M','S')
#define FOURCC_ICMT mmioFOURCC('I','C','M','T')
#define FOURCC_ICOP mmioFOURCC('I','C','O','P')
#define FOURCC_ICRD mmioFOURCC('I','C','R','D')
#define FOURCC_IENG mmioFOURCC('I','E','N','G')
#define FOURCC_IGNR mmioFOURCC('I','G','N','R')
#define FOURCC_IKEY mmioFOURCC('I','K','E','Y')
#define FOURCC_IMED mmioFOURCC('I','M','E','D')
#define FOURCC_INAM mmioFOURCC('I','N','A','M')
#define FOURCC_IPRD mmioFOURCC('I','P','R','D')
#define FOURCC_ISBJ mmioFOURCC('I','S','B','J')
#define FOURCC_ISFT mmioFOURCC('I','S','F','T')
#define FOURCC_ISRC mmioFOURCC('I','S','R','C')
#define FOURCC_ISRF mmioFOURCC('I','S','R','F')
#define FOURCC_ITCH mmioFOURCC('I','T','C','H')
static void FreeRegions(DLS_Instrument *instrument)
{
if ( instrument->regions ) {
free(instrument->regions);
}
}
static void AllocRegions(DLS_Instrument *instrument)
{
int datalen = (instrument->header->cRegions * sizeof(DLS_Region));
FreeRegions(instrument);
instrument->regions = (DLS_Region *)malloc(datalen);
if ( instrument->regions ) {
memset(instrument->regions, 0, datalen);
}
}
static void FreeInstruments(DLS_Data *data)
{
if ( data->instruments ) {
DWORD i;
for ( i = 0; i < data->cInstruments; ++i ) {
FreeRegions(&data->instruments[i]);
}
free(data->instruments);
}
}
static void AllocInstruments(DLS_Data *data)
{
int datalen = (data->cInstruments * sizeof(DLS_Instrument));
FreeInstruments(data);
data->instruments = (DLS_Instrument *)malloc(datalen);
if ( data->instruments ) {
memset(data->instruments, 0, datalen);
}
}
static void FreeWaveList(DLS_Data *data)
{
if ( data->waveList ) {
free(data->waveList);
}
}
static void AllocWaveList(DLS_Data *data)
{
int datalen = (data->ptbl->cCues * sizeof(DLS_Wave));
FreeWaveList(data);
data->waveList = (DLS_Wave *)malloc(datalen);
if ( data->waveList ) {
memset(data->waveList, 0, datalen);
}
}
static void Parse_colh(DLS_Data *data, RIFF_Chunk *chunk)
{
data->cInstruments = LittleLong(*(DWORD *)chunk->data);
AllocInstruments(data);
}
static void Parse_insh(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument)
{
INSTHEADER *header = (INSTHEADER *)chunk->data;
header->cRegions = LittleLong(header->cRegions);
header->Locale.ulBank = LittleLong(header->Locale.ulBank);
header->Locale.ulInstrument = LittleLong(header->Locale.ulInstrument);
instrument->header = header;
AllocRegions(instrument);
}
static void Parse_rgnh(DLS_Data *data, RIFF_Chunk *chunk, DLS_Region *region)
{
RGNHEADER *header = (RGNHEADER *)chunk->data;
header->RangeKey.usLow = LittleShort(header->RangeKey.usLow);
header->RangeKey.usHigh = LittleShort(header->RangeKey.usHigh);
header->RangeVelocity.usLow = LittleShort(header->RangeVelocity.usLow);
header->RangeVelocity.usHigh = LittleShort(header->RangeVelocity.usHigh);
header->fusOptions = LittleShort(header->fusOptions);
header->usKeyGroup = LittleShort(header->usKeyGroup);
region->header = header;
}
static void Parse_wlnk(DLS_Data *data, RIFF_Chunk *chunk, DLS_Region *region)
{
WAVELINK *wlnk = (WAVELINK *)chunk->data;
wlnk->fusOptions = LittleShort(wlnk->fusOptions);
wlnk->usPhaseGroup = LittleShort(wlnk->usPhaseGroup);
wlnk->ulChannel = LittleLong((unsigned int)wlnk->ulChannel);
wlnk->ulTableIndex = LittleLong((unsigned int)wlnk->ulTableIndex);
region->wlnk = wlnk;
}
static void Parse_wsmp(DLS_Data *data, RIFF_Chunk *chunk, WSMPL **wsmp_ptr, WLOOP **wsmp_loop_ptr)
{
DWORD i;
WSMPL *wsmp = (WSMPL *)chunk->data;
WLOOP *loop;
wsmp->cbSize = LittleLong(wsmp->cbSize);
wsmp->usUnityNote = LittleShort(wsmp->usUnityNote);
wsmp->sFineTune = LittleShort(wsmp->sFineTune);
wsmp->lAttenuation = LittleLong(wsmp->lAttenuation);
wsmp->fulOptions = LittleLong(wsmp->fulOptions);
wsmp->cSampleLoops = LittleLong(wsmp->cSampleLoops);
loop = (WLOOP *)((BYTE *)chunk->data + wsmp->cbSize);
*wsmp_ptr = wsmp;
*wsmp_loop_ptr = loop;
for ( i = 0; i < wsmp->cSampleLoops; ++i ) {
loop->cbSize = LittleLong(loop->cbSize);
loop->ulType = LittleLong(loop->ulType);
loop->ulStart = LittleLong(loop->ulStart);
loop->ulLength = LittleLong(loop->ulLength);
++loop;
}
}
static void Parse_art(DLS_Data *data, RIFF_Chunk *chunk, CONNECTIONLIST **art_ptr, CONNECTION **artList_ptr)
{
DWORD i;
CONNECTIONLIST *art = (CONNECTIONLIST *)chunk->data;
CONNECTION *artList;
art->cbSize = LittleLong(art->cbSize);
art->cConnections = LittleLong(art->cConnections);
artList = (CONNECTION *)((BYTE *)chunk->data + art->cbSize);
*art_ptr = art;
*artList_ptr = artList;
for ( i = 0; i < art->cConnections; ++i ) {
artList->usSource = LittleShort(artList->usSource);
artList->usControl = LittleShort(artList->usControl);
artList->usDestination = LittleShort(artList->usDestination);
artList->usTransform = LittleShort(artList->usTransform);
artList->lScale = LittleLong(artList->lScale);
++artList;
}
}
static void Parse_lart(DLS_Data *data, RIFF_Chunk *chunk, CONNECTIONLIST **conn_ptr, CONNECTION **connList_ptr)
{
/* FIXME: This only supports one set of connections */
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_ART1:
case FOURCC_ART2:
Parse_art(data, chunk, conn_ptr, connList_ptr);
return;
}
}
}
static void Parse_rgn(DLS_Data *data, RIFF_Chunk *chunk, DLS_Region *region)
{
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_RGNH:
Parse_rgnh(data, chunk, region);
break;
case FOURCC_WLNK:
Parse_wlnk(data, chunk, region);
break;
case FOURCC_WSMP:
Parse_wsmp(data, chunk, &region->wsmp, &region->wsmp_loop);
break;
case FOURCC_LART:
case FOURCC_LAR2:
Parse_lart(data, chunk, &region->art, &region->artList);
break;
}
}
}
static void Parse_lrgn(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument)
{
DWORD region = 0;
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_RGN:
case FOURCC_RGN2:
if ( region < instrument->header->cRegions ) {
Parse_rgn(data, chunk, &instrument->regions[region++]);
}
break;
}
}
}
static void Parse_INFO_INS(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument)
{
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_INAM: /* Name */
instrument->name = (const char *)chunk->data;
break;
}
}
}
static void Parse_ins(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument)
{
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_INSH:
Parse_insh(data, chunk, instrument);
break;
case FOURCC_LRGN:
Parse_lrgn(data, chunk, instrument);
break;
case FOURCC_LART:
case FOURCC_LAR2:
Parse_lart(data, chunk, &instrument->art, &instrument->artList);
break;
case FOURCC_INFO:
Parse_INFO_INS(data, chunk, instrument);
break;
}
}
}
static void Parse_lins(DLS_Data *data, RIFF_Chunk *chunk)
{
DWORD instrument = 0;
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_INS:
if ( instrument < data->cInstruments ) {
Parse_ins(data, chunk, &data->instruments[instrument++]);
}
break;
}
}
}
static void Parse_ptbl(DLS_Data *data, RIFF_Chunk *chunk)
{
DWORD i;
POOLTABLE *ptbl = (POOLTABLE *)chunk->data;
ptbl->cbSize = LittleLong(ptbl->cbSize);
ptbl->cCues = LittleLong(ptbl->cCues);
data->ptbl = ptbl;
data->ptblList = (POOLCUE *)((BYTE *)chunk->data + ptbl->cbSize);
for ( i = 0; i < ptbl->cCues; ++i ) {
data->ptblList[i].ulOffset = LittleLong(data->ptblList[i].ulOffset);
}
AllocWaveList(data);
}
static void Parse_fmt(DLS_Data *data, RIFF_Chunk *chunk, DLS_Wave *wave)
{
WaveFMT *fmt = (WaveFMT *)chunk->data;
fmt->wFormatTag = LittleShort(fmt->wFormatTag);
fmt->wChannels = LittleShort(fmt->wChannels);
fmt->dwSamplesPerSec = LittleLong(fmt->dwSamplesPerSec);
fmt->dwAvgBytesPerSec = LittleLong(fmt->dwAvgBytesPerSec);
fmt->wBlockAlign = LittleShort(fmt->wBlockAlign);
fmt->wBitsPerSample = LittleShort(fmt->wBitsPerSample);
wave->format = fmt;
}
static void Parse_data(DLS_Data *data, RIFF_Chunk *chunk, DLS_Wave *wave)
{
wave->data = chunk->data;
wave->length = chunk->length;
}
static void Parse_wave(DLS_Data *data, RIFF_Chunk *chunk, DLS_Wave *wave)
{
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_FMT:
Parse_fmt(data, chunk, wave);
break;
case FOURCC_DATA:
Parse_data(data, chunk, wave);
break;
case FOURCC_WSMP:
Parse_wsmp(data, chunk, &wave->wsmp, &wave->wsmp_loop);
break;
}
}
}
static void Parse_wvpl(DLS_Data *data, RIFF_Chunk *chunk)
{
DWORD wave = 0;
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_wave:
if ( wave < data->ptbl->cCues ) {
Parse_wave(data, chunk, &data->waveList[wave++]);
}
break;
}
}
}
static void Parse_INFO_DLS(DLS_Data *data, RIFF_Chunk *chunk)
{
for ( chunk = chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_IARL: /* Archival Location */
break;
case FOURCC_IART: /* Artist */
data->artist = (const char *)chunk->data;
break;
case FOURCC_ICMS: /* Commisioned */
break;
case FOURCC_ICMT: /* Comments */
data->comments = (const char *)chunk->data;
break;
case FOURCC_ICOP: /* Copyright */
data->copyright = (const char *)chunk->data;
break;
case FOURCC_ICRD: /* Creation Date */
break;
case FOURCC_IENG: /* Engineer */
break;
case FOURCC_IGNR: /* Genre */
break;
case FOURCC_IKEY: /* Keywords */
break;
case FOURCC_IMED: /* Medium */
break;
case FOURCC_INAM: /* Name */
data->name = (const char *)chunk->data;
break;
case FOURCC_IPRD: /* Product */
break;
case FOURCC_ISBJ: /* Subject */
break;
case FOURCC_ISFT: /* Software */
break;
case FOURCC_ISRC: /* Source */
break;
case FOURCC_ISRF: /* Source Form */
break;
case FOURCC_ITCH: /* Technician */
break;
}
}
}
DLS_Data *LoadDLS(FILE *src)
{
RIFF_Chunk *chunk;
DLS_Data *data = (DLS_Data *)malloc(sizeof(*data));
if ( !data ) {
__Sound_SetError(ERR_OUT_OF_MEMORY);
return NULL;
}
memset(data, 0, sizeof(*data));
data->chunk = LoadRIFF(src);
if ( !data->chunk ) {
FreeDLS(data);
return NULL;
}
for ( chunk = data->chunk->child; chunk; chunk = chunk->next ) {
DWORD magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic;
switch(magic) {
case FOURCC_COLH:
Parse_colh(data, chunk);
break;
case FOURCC_LINS:
Parse_lins(data, chunk);
break;
case FOURCC_PTBL:
Parse_ptbl(data, chunk);
break;
case FOURCC_WVPL:
Parse_wvpl(data, chunk);
break;
case FOURCC_INFO:
Parse_INFO_DLS(data, chunk);
break;
}
}
return data;
}
void FreeDLS(DLS_Data *data)
{
if ( data->chunk ) {
FreeRIFF(data->chunk);
}
FreeInstruments(data);
FreeWaveList(data);
free(data);
}
static const char *SourceToString(USHORT usSource)
{
static char unknown[32];
switch(usSource) {
case CONN_SRC_NONE:
return "NONE";
case CONN_SRC_LFO:
return "LFO";
case CONN_SRC_KEYONVELOCITY:
return "KEYONVELOCITY";
case CONN_SRC_KEYNUMBER:
return "KEYNUMBER";
case CONN_SRC_EG1:
return "EG1";
case CONN_SRC_EG2:
return "EG2";
case CONN_SRC_PITCHWHEEL:
return "PITCHWHEEL";
case CONN_SRC_CC1:
return "CC1";
case CONN_SRC_CC7:
return "CC7";
case CONN_SRC_CC10:
return "CC10";
case CONN_SRC_CC11:
return "CC11";
case CONN_SRC_POLYPRESSURE:
return "POLYPRESSURE";
case CONN_SRC_CHANNELPRESSURE:
return "CHANNELPRESSURE";
case CONN_SRC_VIBRATO:
return "VIBRATO";
case CONN_SRC_MONOPRESSURE:
return "MONOPRESSURE";
case CONN_SRC_CC91:
return "CC91";
case CONN_SRC_CC93:
return "CC93";
default:
mysnprintf(unknown, countof(unknown), "UNKNOWN (0x%04x)", usSource);
return unknown;
}
}
static const char *TransformToString(USHORT usTransform)
{
static char unknown[32];
switch (usTransform) {
case CONN_TRN_NONE:
return "NONE";
case CONN_TRN_CONCAVE:
return "CONCAVE";
case CONN_TRN_CONVEX:
return "CONVEX";
case CONN_TRN_SWITCH:
return "SWITCH";
default:
mysnprintf(unknown, countof(unknown), "UNKNOWN (0x%04x)", usTransform);
return unknown;
}
}
static const char *DestinationToString(USHORT usDestination)
{
static char unknown[32];
switch (usDestination) {
case CONN_DST_NONE:
return "NONE";
case CONN_DST_ATTENUATION:
return "ATTENUATION";
case CONN_DST_PITCH:
return "PITCH";
case CONN_DST_PAN:
return "PAN";
case CONN_DST_LFO_FREQUENCY:
return "LFO_FREQUENCY";
case CONN_DST_LFO_STARTDELAY:
return "LFO_STARTDELAY";
case CONN_DST_EG1_ATTACKTIME:
return "EG1_ATTACKTIME";
case CONN_DST_EG1_DECAYTIME:
return "EG1_DECAYTIME";
case CONN_DST_EG1_RELEASETIME:
return "EG1_RELEASETIME";
case CONN_DST_EG1_SUSTAINLEVEL:
return "EG1_SUSTAINLEVEL";
case CONN_DST_EG2_ATTACKTIME:
return "EG2_ATTACKTIME";
case CONN_DST_EG2_DECAYTIME:
return "EG2_DECAYTIME";
case CONN_DST_EG2_RELEASETIME:
return "EG2_RELEASETIME";
case CONN_DST_EG2_SUSTAINLEVEL:
return "EG2_SUSTAINLEVEL";
case CONN_DST_KEYNUMBER:
return "KEYNUMBER";
case CONN_DST_LEFT:
return "LEFT";
case CONN_DST_RIGHT:
return "RIGHT";
case CONN_DST_CENTER:
return "CENTER";
case CONN_DST_LEFTREAR:
return "LEFTREAR";
case CONN_DST_RIGHTREAR:
return "RIGHTREAR";
case CONN_DST_LFE_CHANNEL:
return "LFE_CHANNEL";
case CONN_DST_CHORUS:
return "CHORUS";
case CONN_DST_REVERB:
return "REVERB";
case CONN_DST_VIB_FREQUENCY:
return "VIB_FREQUENCY";
case CONN_DST_VIB_STARTDELAY:
return "VIB_STARTDELAY";
case CONN_DST_EG1_DELAYTIME:
return "EG1_DELAYTIME";
case CONN_DST_EG1_HOLDTIME:
return "EG1_HOLDTIME";
case CONN_DST_EG1_SHUTDOWNTIME:
return "EG1_SHUTDOWNTIME";
case CONN_DST_EG2_DELAYTIME:
return "EG2_DELAYTIME";
case CONN_DST_EG2_HOLDTIME:
return "EG2_HOLDTIME";
case CONN_DST_FILTER_CUTOFF:
return "FILTER_CUTOFF";
case CONN_DST_FILTER_Q:
return "FILTER_Q";
default:
mysnprintf(unknown, countof(unknown), "UNKNOWN (0x%04x)", usDestination);
return unknown;
}
}
static void PrintArt(const char *type, CONNECTIONLIST *art, CONNECTION *artList)
{
DWORD i;
printf("%s Connections:\n", type);
for ( i = 0; i < art->cConnections; ++i ) {
printf(" Source: %s, Control: %s, Destination: %s, Transform: %s, Scale: %d\n",
SourceToString(artList[i].usSource),
SourceToString(artList[i].usControl),
DestinationToString(artList[i].usDestination),
TransformToString(artList[i].usTransform),
artList[i].lScale);
}
}
static void PrintWave(DLS_Wave *wave, DWORD index)
{
WaveFMT *format = wave->format;
if ( format ) {
printf(" Wave %u: Format: %hu, %hu channels, %u Hz, %hu bits (length = %u)\n", index, format->wFormatTag, format->wChannels, format->dwSamplesPerSec, format->wBitsPerSample, wave->length);
}
if ( wave->wsmp ) {
DWORD i;
printf(" wsmp->usUnityNote = %hu\n", wave->wsmp->usUnityNote);
printf(" wsmp->sFineTune = %hd\n", wave->wsmp->sFineTune);
printf(" wsmp->lAttenuation = %d\n", wave->wsmp->lAttenuation);
printf(" wsmp->fulOptions = 0x%8.8x\n", wave->wsmp->fulOptions);
printf(" wsmp->cSampleLoops = %u\n", wave->wsmp->cSampleLoops);
for ( i = 0; i < wave->wsmp->cSampleLoops; ++i ) {
WLOOP *loop = &wave->wsmp_loop[i];
printf(" Loop %u:\n", i);
printf(" ulStart = %u\n", loop->ulStart);
printf(" ulLength = %u\n", loop->ulLength);
}
}
}
static void PrintRegion(DLS_Region *region, DWORD index)
{
printf(" Region %u:\n", index);
if ( region->header ) {
printf(" RangeKey = { %hu - %hu }\n", region->header->RangeKey.usLow, region->header->RangeKey.usHigh);
printf(" RangeVelocity = { %hu - %hu }\n", region->header->RangeVelocity.usLow, region->header->RangeVelocity.usHigh);
printf(" fusOptions = 0x%4.4hx\n", region->header->fusOptions);
printf(" usKeyGroup = %hu\n", region->header->usKeyGroup);
}
if ( region->wlnk ) {
printf(" wlnk->fusOptions = 0x%4.4hx\n", region->wlnk->fusOptions);
printf(" wlnk->usPhaseGroup = %hu\n", region->wlnk->usPhaseGroup);
printf(" wlnk->ulChannel = %u\n", region->wlnk->ulChannel);
printf(" wlnk->ulTableIndex = %u\n", region->wlnk->ulTableIndex);
}
if ( region->wsmp ) {
DWORD i;
printf(" wsmp->usUnityNote = %hu\n", region->wsmp->usUnityNote);
printf(" wsmp->sFineTune = %hd\n", region->wsmp->sFineTune);
printf(" wsmp->lAttenuation = %d\n", region->wsmp->lAttenuation);
printf(" wsmp->fulOptions = 0x%8.8x\n", region->wsmp->fulOptions);
printf(" wsmp->cSampleLoops = %u\n", region->wsmp->cSampleLoops);
for ( i = 0; i < region->wsmp->cSampleLoops; ++i ) {
WLOOP *loop = &region->wsmp_loop[i];
printf(" Loop %u:\n", i);
printf(" ulStart = %u\n", loop->ulStart);
printf(" ulLength = %u\n", loop->ulLength);
}
}
if ( region->art && region->art->cConnections > 0 ) {
PrintArt("Region", region->art, region->artList);
}
}
static void PrintInstrument(DLS_Instrument *instrument, DWORD index)
{
printf("Instrument %u:\n", index);
if ( instrument->name ) {
printf(" Name: %s\n", instrument->name);
}
if ( instrument->header ) {
DWORD i;
printf(" ulBank = 0x%8.8x\n", instrument->header->Locale.ulBank);
printf(" ulInstrument = %u\n", instrument->header->Locale.ulInstrument);
printf(" Regions: %u\n", instrument->header->cRegions);
for ( i = 0; i < instrument->header->cRegions; ++i ) {
PrintRegion(&instrument->regions[i], i);
}
}
if ( instrument->art && instrument->art->cConnections > 0 ) {
PrintArt("Instrument", instrument->art, instrument->artList);
}
};
void PrintDLS(DLS_Data *data)
{
printf("DLS Data:\n");
printf("cInstruments = %u\n", data->cInstruments);
if ( data->instruments ) {
DWORD i;
for ( i = 0; i < data->cInstruments; ++i ) {
PrintInstrument(&data->instruments[i], i);
}
}
if ( data->ptbl && data->ptbl->cCues > 0 ) {
DWORD i;
printf("Cues: ");
for ( i = 0; i < data->ptbl->cCues; ++i ) {
if ( i > 0 ) {
printf(", ");
}
printf("%u", data->ptblList[i].ulOffset);
}
printf("\n");
}
if ( data->waveList && data->ptbl ) {
DWORD i;
printf("Waves:\n");
for ( i = 0; i < data->ptbl->cCues; ++i ) {
PrintWave(&data->waveList[i], i);
}
}
if ( data->name ) {
printf("Name: %s\n", data->name);
}
if ( data->artist ) {
printf("Artist: %s\n", data->artist);
}
if ( data->copyright ) {
printf("Copyright: %s\n", data->copyright);
}
if ( data->comments ) {
printf("Comments: %s\n", data->comments);
}
}
#ifdef TEST_MAIN_DLS
}
int main(int argc, char *argv[])
{
int i;
for ( i = 1; i < argc; ++i ) {
Timidity::DLS_Data *data;
FILE *src = fopen(argv[i], "rb");
if ( !src ) {
fprintf(stderr, "Couldn't open %s: %s", argv[i], strerror(errno));
continue;
}
data = Timidity::LoadDLS(src);
if ( data ) {
Timidity::PrintRIFF(data->chunk, 0);
Timidity::PrintDLS(data);
Timidity::FreeDLS(data);
} else {
fprintf(stderr, "Couldn't load %s: %s\n", argv[i], strerror(errno));
}
fclose(src);
}
return 0;
}
namespace Timidity
{
#endif // TEST_MAIN
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
/* * * * * * * * * * * * * * * * * instrum_dls.c * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------*/
#ifndef TEST_MAIN_DLS
DLS_Data *Timidity_LoadDLS(FILE *src)
{
return LoadDLS(src);
}
void Timidity_FreeDLS(DLS_Data *patches)
{
FreeDLS(patches);
}
/* convert timecents to sec */
static double to_msec(int timecent)
{
if (timecent == INT_MIN || timecent == 0)
return 0.0;
return 1000.0 * pow(2.0, (double)(timecent / 65536) / 1200.0);
}
/* convert decipercent to {0..1} */
static double to_normalized_percent(int decipercent)
{
return ((double)(decipercent / 65536)) / 1000.0;
}
/* convert from 8bit value to fractional offset (15.15) */
static SDWORD to_offset(int offset)
{
return (SDWORD)offset << (7+15);
}
/* calculate ramp rate in fractional unit;
* diff = 8bit, time = msec
*/
static SDWORD calc_rate(Renderer *song, int diff, int sample_rate, double msec)
{
double rate;
if(msec < 6)
msec = 6;
if(diff == 0)
diff = 255;
diff <<= (7+15);
rate = ((double)diff / song->rate) * song->control_ratio * 1000.0 / msec;
return (SDWORD)rate;
}
static int load_connection(ULONG cConnections, CONNECTION *artList, USHORT destination)
{
ULONG i;
int value = 0;
for (i = 0; i < cConnections; ++i) {
CONNECTION *conn = &artList[i];
if(conn->usDestination == destination) {
// The formula for the destination is:
// usDestination = usDestination + usTransform(usSource * (usControl * lScale))
// Since we are only handling source/control of NONE and identity
// transform, this simplifies to: usDestination = usDestination + lScale
if (conn->usSource == CONN_SRC_NONE &&
conn->usControl == CONN_SRC_NONE &&
conn->usTransform == CONN_TRN_NONE)
value += conn->lScale;
}
}
return value;
}
static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, DWORD index)
{
DLS_Region *rgn = &ins->regions[index];
DLS_Wave *wave = &song->patches->waveList[rgn->wlnk->ulTableIndex];
sample->type = INST_DLS;
sample->self_nonexclusive = !!(rgn->header->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE);
sample->key_group = (SBYTE)rgn->header->usKeyGroup;
sample->low_freq = note_to_freq(rgn->header->RangeKey.usLow);
sample->high_freq = note_to_freq(rgn->header->RangeKey.usHigh);
sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote + rgn->wsmp->sFineTune * .01f);
sample->low_vel = (BYTE)rgn->header->RangeVelocity.usLow;
sample->high_vel = (BYTE)rgn->header->RangeVelocity.usHigh;
sample->modes = wave->format->wBitsPerSample == 8 ? PATCH_UNSIGNED : PATCH_16;
sample->sample_rate = wave->format->dwSamplesPerSec;
sample->data = NULL;
sample->data_length = wave->length;
convert_sample_data(sample, wave->data);
if (rgn->wsmp->cSampleLoops)
{
sample->modes |= (PATCH_LOOPEN | PATCH_SUSTAIN/* | PATCH_NO_SRELEASE*/);
sample->loop_start = rgn->wsmp_loop->ulStart / 2;
sample->loop_end = sample->loop_start + (rgn->wsmp_loop->ulLength / 2);
}
sample->scale_factor = 1024;
sample->scale_note = rgn->wsmp->usUnityNote;
if (sample->modes & PATCH_SUSTAIN)
{
int value;
int attack, hold, decay, release; int sustain;
CONNECTIONLIST *art = NULL;
CONNECTION *artList = NULL;
if (ins->art && ins->art->cConnections > 0 && ins->artList) {
art = ins->art;
artList = ins->artList;
} else {
art = rgn->art;
artList = rgn->artList;
}
attack = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME);
hold = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME);
decay = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME);
release = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME);
sustain = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL);
value = load_connection(art->cConnections, artList, CONN_DST_PAN);
sample->panning = (int)((0.5 + to_normalized_percent(value)) * 16383.f);
/*
printf("%d, Rate=%d LV=%d HV=%d Low=%d Hi=%d Root=%d Pan=%d Attack=%f Hold=%f Sustain=%d Decay=%f Release=%f\n", index, sample->sample_rate, rgn->header->RangeVelocity.usLow, rgn->header->RangeVelocity.usHigh, sample->low_freq, sample->high_freq, sample->root_freq, sample->panning, attack, hold, sustain, decay, release);
*/
sample->envelope.sf2.delay_vol = -32768;
sample->envelope.sf2.attack_vol = (short)(attack >> 16);
sample->envelope.sf2.hold_vol = (short)(hold >> 16);
sample->envelope.sf2.decay_vol = (short)(decay >> 16);
sample->envelope.sf2.release_vol = (short)(release >> 16);
sample->envelope.sf2.sustain_vol = (short)(sustain >> 16);
}
sample->data_length <<= FRACTION_BITS;
sample->loop_start <<= FRACTION_BITS;
sample->loop_end <<= FRACTION_BITS;
}
Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument)
{
Instrument *inst;
DWORD i;
DLS_Instrument *dls_ins = NULL;
if (song->patches == NULL)
{
return NULL;
}
drum = drum ? 0x80000000 : 0;
for (i = 0; i < song->patches->cInstruments; ++i)
{
dls_ins = &song->patches->instruments[i];
if ((dls_ins->header->Locale.ulBank & 0x80000000) == (ULONG)drum &&
((dls_ins->header->Locale.ulBank >> 8) & 0xFF) == (ULONG)bank &&
dls_ins->header->Locale.ulInstrument == (ULONG)instrument)
break;
}
if (i == song->patches->cInstruments && bank == 0)
{
for (i = 0; i < song->patches->cInstruments; ++i)
{
dls_ins = &song->patches->instruments[i];
if ((dls_ins->header->Locale.ulBank & 0x80000000) == (ULONG)drum &&
dls_ins->header->Locale.ulInstrument == (ULONG)instrument)
break;
}
}
if (i == song->patches->cInstruments)
{
// SNDDBG(("Couldn't find %s instrument %d in bank %d\n", drum ? "drum" : "melodic", instrument, bank));
return NULL;
}
inst = (Instrument *)safe_malloc(sizeof(Instrument));
inst->samples = dls_ins->header->cRegions;
inst->sample = (Sample *)safe_malloc(inst->samples * sizeof(Sample));
memset(inst->sample, 0, inst->samples * sizeof(Sample));
/*
printf("Found %s instrument %d in bank %d named %s with %d regions\n", drum ? "drum" : "melodic", instrument, bank, dls_ins->name, inst->samples);
*/
for (i = 0; i < dls_ins->header->cRegions; ++i)
{
load_region_dls(song, &inst->sample[i], dls_ins, i);
}
return inst;
}
#endif /* !TEST_MAIN_DLS */
}