2008-04-11 04:59:23 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
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 = LittleShort(wlnk->ulChannel);
|
|
|
|
wlnk->ulTableIndex = LittleShort(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, ®ion->wsmp, ®ion->wsmp_loop);
|
|
|
|
break;
|
|
|
|
case FOURCC_LART:
|
|
|
|
case FOURCC_LAR2:
|
|
|
|
Parse_lart(data, chunk, ®ion->art, ®ion->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:
|
|
|
|
sprintf(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:
|
|
|
|
sprintf(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:
|
|
|
|
sprintf(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 = ®ion->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 ) {
|
|
|
|
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)
|
|
|
|
{
|
2008-04-12 05:33:20 +00:00
|
|
|
if (timecent == INT_MIN || timecent == 0)
|
2008-04-11 04:59:23 +00:00
|
|
|
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];
|
|
|
|
|
2008-04-16 05:41:03 +00:00
|
|
|
sample->self_nonexclusive = !!(rgn->header->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE);
|
|
|
|
sample->key_group = (SBYTE)rgn->header->usKeyGroup;
|
2008-04-11 04:59:23 +00:00
|
|
|
sample->low_freq = SDWORD(note_to_freq(rgn->header->RangeKey.usLow));
|
|
|
|
sample->high_freq = SDWORD(note_to_freq(rgn->header->RangeKey.usHigh));
|
|
|
|
sample->root_freq = SDWORD(note_to_freq(rgn->wsmp->usUnityNote));
|
|
|
|
sample->low_vel = rgn->header->RangeVelocity.usLow;
|
|
|
|
sample->high_vel = rgn->header->RangeVelocity.usHigh;
|
|
|
|
|
2008-04-16 05:41:03 +00:00
|
|
|
sample->modes = wave->format->wBitsPerSample == 8 ? PATCH_UNSIGNED : PATCH_16;
|
2008-04-11 04:59:23 +00:00
|
|
|
sample->sample_rate = wave->format->dwSamplesPerSec;
|
|
|
|
sample->data = NULL;
|
|
|
|
sample->data_length = wave->length;
|
|
|
|
convert_sample_data(sample, wave->data);
|
2008-04-16 05:41:03 +00:00
|
|
|
if (rgn->wsmp->cSampleLoops)
|
|
|
|
{
|
|
|
|
sample->modes |= (PATCH_LOOPEN | PATCH_SUSTAIN);
|
2008-04-11 04:59:23 +00:00
|
|
|
sample->loop_start = rgn->wsmp_loop->ulStart / 2;
|
|
|
|
sample->loop_end = sample->loop_start + (rgn->wsmp_loop->ulLength / 2);
|
|
|
|
}
|
|
|
|
sample->volume = 1.0f;
|
|
|
|
|
2008-04-16 05:41:03 +00:00
|
|
|
if (sample->modes & PATCH_SUSTAIN)
|
|
|
|
{
|
2008-04-11 04:59:23 +00:00
|
|
|
int value;
|
|
|
|
double 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME);
|
|
|
|
attack = to_msec(value);
|
|
|
|
value = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME);
|
|
|
|
hold = to_msec(value);
|
|
|
|
value = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME);
|
|
|
|
decay = to_msec(value);
|
|
|
|
value = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME);
|
|
|
|
release = to_msec(value);
|
|
|
|
value = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL);
|
|
|
|
sustain = (int)((1.0 - to_normalized_percent(value)) * 250.0);
|
|
|
|
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_offset[ATTACK] = to_offset(255);
|
|
|
|
sample->envelope_rate[ATTACK] = calc_rate(song, 255, sample->sample_rate, attack);
|
|
|
|
|
|
|
|
sample->envelope_offset[HOLD] = to_offset(250);
|
|
|
|
sample->envelope_rate[HOLD] = calc_rate(song, 5, sample->sample_rate, hold);
|
|
|
|
|
|
|
|
sample->envelope_offset[DECAY] = to_offset(sustain);
|
|
|
|
sample->envelope_rate[DECAY] = calc_rate(song, 255 - sustain, sample->sample_rate, decay);
|
|
|
|
|
|
|
|
sample->envelope_offset[RELEASE] = to_offset(0);
|
|
|
|
sample->envelope_rate[RELEASE] = calc_rate(song, 5 + sustain, sample->sample_rate, release);
|
|
|
|
|
|
|
|
sample->envelope_offset[RELEASEB] = to_offset(0);
|
|
|
|
sample->envelope_rate[RELEASEB] = to_offset(1);
|
|
|
|
|
|
|
|
sample->envelope_offset[RELEASEC] = to_offset(0);
|
|
|
|
sample->envelope_rate[RELEASEC] = to_offset(1);
|
|
|
|
|
2008-04-16 05:41:03 +00:00
|
|
|
sample->modes |= PATCH_NO_SRELEASE;
|
2008-04-11 04:59:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sample->data_length <<= FRACTION_BITS;
|
|
|
|
sample->loop_start <<= FRACTION_BITS;
|
|
|
|
sample->loop_end <<= FRACTION_BITS;
|
|
|
|
}
|
|
|
|
|
2008-04-16 05:41:03 +00:00
|
|
|
Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument)
|
2008-04-11 04:59:23 +00:00
|
|
|
{
|
2008-04-16 05:41:03 +00:00
|
|
|
Instrument *inst;
|
2008-04-11 04:59:23 +00:00
|
|
|
DWORD i;
|
2008-04-12 05:33:20 +00:00
|
|
|
DLS_Instrument *dls_ins = NULL;
|
2008-04-11 04:59:23 +00:00
|
|
|
|
|
|
|
if (song->patches == NULL)
|
2008-04-16 05:41:03 +00:00
|
|
|
{
|
2008-04-11 04:59:23 +00:00
|
|
|
return NULL;
|
2008-04-16 05:41:03 +00:00
|
|
|
}
|
2008-04-11 04:59:23 +00:00
|
|
|
drum = drum ? 0x80000000 : 0;
|
2008-04-16 05:41:03 +00:00
|
|
|
for (i = 0; i < song->patches->cInstruments; ++i)
|
|
|
|
{
|
2008-04-11 04:59:23 +00:00
|
|
|
dls_ins = &song->patches->instruments[i];
|
2008-04-12 05:33:20 +00:00
|
|
|
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)
|
2008-04-11 04:59:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-04-16 05:41:03 +00:00
|
|
|
if (i == song->patches->cInstruments && bank == 0)
|
|
|
|
{
|
|
|
|
for (i = 0; i < song->patches->cInstruments; ++i)
|
|
|
|
{
|
2008-04-11 04:59:23 +00:00
|
|
|
dls_ins = &song->patches->instruments[i];
|
2008-04-12 05:33:20 +00:00
|
|
|
if ((dls_ins->header->Locale.ulBank & 0x80000000) == (ULONG)drum &&
|
|
|
|
dls_ins->header->Locale.ulInstrument == (ULONG)instrument)
|
2008-04-11 04:59:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-04-16 05:41:03 +00:00
|
|
|
if (i == song->patches->cInstruments)
|
|
|
|
{
|
2008-04-11 04:59:23 +00:00
|
|
|
// SNDDBG(("Couldn't find %s instrument %d in bank %d\n", drum ? "drum" : "melodic", instrument, bank));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-04-16 05:41:03 +00:00
|
|
|
inst = (Instrument *)safe_malloc(sizeof(Instrument));
|
|
|
|
inst->type = INST_DLS;
|
|
|
|
inst->samples = dls_ins->header->cRegions;
|
|
|
|
inst->sample = (Sample *)safe_malloc(inst->samples * sizeof(Sample));
|
|
|
|
memset(inst->sample, 0, inst->samples * sizeof(Sample));
|
2008-04-11 04:59:23 +00:00
|
|
|
/*
|
|
|
|
printf("Found %s instrument %d in bank %d named %s with %d regions\n", drum ? "drum" : "melodic", instrument, bank, dls_ins->name, inst->samples);
|
|
|
|
*/
|
2008-04-16 05:41:03 +00:00
|
|
|
for (i = 0; i < dls_ins->header->cRegions; ++i)
|
|
|
|
{
|
|
|
|
load_region_dls(song, &inst->sample[i], dls_ins, i);
|
2008-04-11 04:59:23 +00:00
|
|
|
}
|
2008-04-16 05:41:03 +00:00
|
|
|
return inst;
|
2008-04-11 04:59:23 +00:00
|
|
|
}
|
|
|
|
#endif /* !TEST_MAIN_DLS */
|
|
|
|
|
|
|
|
}
|