Added timidity for use in openttd

This commit is contained in:
pelya
2010-11-16 18:19:46 +02:00
parent 1ca0105303
commit 633ee57739
37 changed files with 8146 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
common.c
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* I guess "rb" should be right for any libc */
#define OPEN_MODE "rb"
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
/* The paths in this list will be tried whenever we're reading a file */
static PathList *pathlist = NULL; /* This is a linked list */
/* This is meant to find and open files for reading */
FILE *open_file(char *name)
{
FILE *fp;
if (!name || !(*name))
{
DEBUG_MSG("Attempted to open nameless file.\n");
return 0;
}
/* First try the given name */
DEBUG_MSG("Trying to open %s\n", name);
if ((fp = fopen(name, OPEN_MODE)))
return fp;
if (name[0] != PATH_SEP)
{
char current_filename[1024];
PathList *plp = pathlist;
int l;
while (plp) /* Try along the path then */
{
*current_filename = 0;
l = strlen(plp->path);
if(l)
{
strcpy(current_filename, plp->path);
if(current_filename[l - 1] != PATH_SEP)
{
current_filename[l] = PATH_SEP;
current_filename[l + 1] = '\0';
}
}
strcat(current_filename, name);
DEBUG_MSG("Trying to open %s\n", current_filename);
if ((fp = fopen(current_filename, OPEN_MODE)))
return fp;
plp = plp->next;
}
}
/* Nothing could be opened. */
DEBUG_MSG("Could not open %s\n", name);
return 0;
}
/* This'll allocate memory or die. */
void *safe_malloc(size_t count)
{
void *p;
p = malloc(count);
if (p == NULL)
DEBUG_MSG("Sorry. Couldn't malloc %d bytes.\n", count);
return p;
}
/* This adds a directory to the path list */
void add_to_pathlist(char *s)
{
PathList *plp = safe_malloc(sizeof(PathList));
if (plp == NULL)
return;
plp->path = safe_malloc(strlen(s) + 1);
if (plp->path == NULL)
{
free(plp);
return;
}
strcpy(plp->path, s);
plp->next = pathlist;
pathlist = plp;
}
void free_pathlist(void)
{
PathList *plp = pathlist;
PathList *next;
while (plp)
{
next = plp->next;
free(plp->path);
free(plp);
plp = next;
}
pathlist = NULL;
}

View File

@@ -0,0 +1,32 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
common.h
*/
typedef struct {
char *path;
void *next;
} PathList;
extern FILE *open_file(char *name);
extern void add_to_pathlist(char *s);
extern void *safe_malloc(size_t count);
extern void free_pathlist(void);

View File

@@ -0,0 +1,266 @@
/*==========================================================================;
//
// dls1.h
//
//
// Description:
//
// Interface defines and structures for the Instrument Collection Form
// RIFF DLS.
//
//
// Written by Sonic Foundry 1996. Released for public use.
//
//=========================================================================*/
#ifndef _INC_DLS1
#define _INC_DLS1
/*//////////////////////////////////////////////////////////////////////////
//
//
// Layout of an instrument collection:
//
//
// RIFF [] 'DLS ' [dlid,colh,INSTLIST,WAVEPOOL,INFOLIST]
//
// INSTLIST
// LIST [] 'lins'
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
//
// RGNLIST
// LIST [] 'lrgn'
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
//
// ARTLIST
// LIST [] 'lart'
// 'art1' level 1 Articulation connection graph
// 'art2' level 2 Articulation connection graph
// '3rd1' Possible 3rd party articulation structure 1
// '3rd2' Possible 3rd party articulation structure 2 .... and so on
//
// WAVEPOOL
// ptbl [] [pool table]
// LIST [] 'wvpl'
// [path],
// [path],
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
//
// INFOLIST
// LIST [] 'INFO'
// 'icmt' 'One of those crazy comments.'
// 'icop' 'Copyright (C) 1996 Sonic Foundry'
//
/////////////////////////////////////////////////////////////////////////*/
/*/////////////////////////////////////////////////////////////////////////
// FOURCC's used in the DLS file
/////////////////////////////////////////////////////////////////////////*/
#define FOURCC_DLS mmioFOURCC('D','L','S',' ')
#define FOURCC_DLID mmioFOURCC('d','l','i','d')
#define FOURCC_COLH mmioFOURCC('c','o','l','h')
#define FOURCC_WVPL mmioFOURCC('w','v','p','l')
#define FOURCC_PTBL mmioFOURCC('p','t','b','l')
#define FOURCC_PATH mmioFOURCC('p','a','t','h')
#define FOURCC_wave mmioFOURCC('w','a','v','e')
#define FOURCC_LINS mmioFOURCC('l','i','n','s')
#define FOURCC_INS mmioFOURCC('i','n','s',' ')
#define FOURCC_INSH mmioFOURCC('i','n','s','h')
#define FOURCC_LRGN mmioFOURCC('l','r','g','n')
#define FOURCC_RGN mmioFOURCC('r','g','n',' ')
#define FOURCC_RGNH mmioFOURCC('r','g','n','h')
#define FOURCC_LART mmioFOURCC('l','a','r','t')
#define FOURCC_ART1 mmioFOURCC('a','r','t','1')
#define FOURCC_WLNK mmioFOURCC('w','l','n','k')
#define FOURCC_WSMP mmioFOURCC('w','s','m','p')
#define FOURCC_VERS mmioFOURCC('v','e','r','s')
/*/////////////////////////////////////////////////////////////////////////
// Articulation connection graph definitions
/////////////////////////////////////////////////////////////////////////*/
/* Generic Sources */
#define CONN_SRC_NONE 0x0000
#define CONN_SRC_LFO 0x0001
#define CONN_SRC_KEYONVELOCITY 0x0002
#define CONN_SRC_KEYNUMBER 0x0003
#define CONN_SRC_EG1 0x0004
#define CONN_SRC_EG2 0x0005
#define CONN_SRC_PITCHWHEEL 0x0006
/* Midi Controllers 0-127 */
#define CONN_SRC_CC1 0x0081
#define CONN_SRC_CC7 0x0087
#define CONN_SRC_CC10 0x008a
#define CONN_SRC_CC11 0x008b
/* Generic Destinations */
#define CONN_DST_NONE 0x0000
#define CONN_DST_ATTENUATION 0x0001
#define CONN_DST_PITCH 0x0003
#define CONN_DST_PAN 0x0004
/* LFO Destinations */
#define CONN_DST_LFO_FREQUENCY 0x0104
#define CONN_DST_LFO_STARTDELAY 0x0105
/* EG1 Destinations */
#define CONN_DST_EG1_ATTACKTIME 0x0206
#define CONN_DST_EG1_DECAYTIME 0x0207
#define CONN_DST_EG1_RELEASETIME 0x0209
#define CONN_DST_EG1_SUSTAINLEVEL 0x020a
/* EG2 Destinations */
#define CONN_DST_EG2_ATTACKTIME 0x030a
#define CONN_DST_EG2_DECAYTIME 0x030b
#define CONN_DST_EG2_RELEASETIME 0x030d
#define CONN_DST_EG2_SUSTAINLEVEL 0x030e
#define CONN_TRN_NONE 0x0000
#define CONN_TRN_CONCAVE 0x0001
typedef struct _DLSID {
ULONG ulData1;
USHORT usData2;
USHORT usData3;
BYTE abData4[8];
} DLSID, FAR *LPDLSID;
typedef struct _DLSVERSION {
DWORD dwVersionMS;
DWORD dwVersionLS;
} DLSVERSION, FAR *LPDLSVERSION;
typedef struct _CONNECTION {
USHORT usSource;
USHORT usControl;
USHORT usDestination;
USHORT usTransform;
LONG lScale;
} CONNECTION, FAR *LPCONNECTION;
/* Level 1 Articulation Data */
typedef struct _CONNECTIONLIST {
ULONG cbSize; /* size of the connection list structure */
ULONG cConnections; /* count of connections in the list */
} CONNECTIONLIST, FAR *LPCONNECTIONLIST;
/*/////////////////////////////////////////////////////////////////////////
// Generic type defines for regions and instruments
/////////////////////////////////////////////////////////////////////////*/
typedef struct _RGNRANGE {
USHORT usLow;
USHORT usHigh;
} RGNRANGE, FAR * LPRGNRANGE;
#define F_INSTRUMENT_DRUMS 0x80000000
typedef struct _MIDILOCALE {
ULONG ulBank;
ULONG ulInstrument;
} MIDILOCALE, FAR *LPMIDILOCALE;
/*/////////////////////////////////////////////////////////////////////////
// Header structures found in an DLS file for collection, instruments, and
// regions.
/////////////////////////////////////////////////////////////////////////*/
#define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001
typedef struct _RGNHEADER {
RGNRANGE RangeKey; /* Key range */
RGNRANGE RangeVelocity; /* Velocity Range */
USHORT fusOptions; /* Synthesis options for this range */
USHORT usKeyGroup; /* Key grouping for non simultaneous play */
/* 0 = no group, 1 up is group */
/* for Level 1 only groups 1-15 are allowed */
} RGNHEADER, FAR *LPRGNHEADER;
typedef struct _INSTHEADER {
ULONG cRegions; /* Count of regions in this instrument */
MIDILOCALE Locale; /* Intended MIDI locale of this instrument */
} INSTHEADER, FAR *LPINSTHEADER;
typedef struct _DLSHEADER {
ULONG cInstruments; /* Count of instruments in the collection */
} DLSHEADER, FAR *LPDLSHEADER;
/*////////////////////////////////////////////////////////////////////////////
// definitions for the Wave link structure
////////////////////////////////////////////////////////////////////////////*/
/* **** For level 1 only WAVELINK_CHANNEL_MONO is valid **** */
/* ulChannel allows for up to 32 channels of audio with each bit position */
/* specifiying a channel of playback */
#define WAVELINK_CHANNEL_LEFT 0x0001l
#define WAVELINK_CHANNEL_RIGHT 0x0002l
#define F_WAVELINK_PHASE_MASTER 0x0001
typedef struct _WAVELINK { /* any paths or links are stored right after struct */
USHORT fusOptions; /* options flags for this wave */
USHORT usPhaseGroup; /* Phase grouping for locking channels */
ULONG ulChannel; /* channel placement */
ULONG ulTableIndex; /* index into the wave pool table, 0 based */
} WAVELINK, FAR *LPWAVELINK;
#define POOL_CUE_NULL 0xffffffffl
typedef struct _POOLCUE {
ULONG ulOffset; /* Offset to the entry in the list */
} POOLCUE, FAR *LPPOOLCUE;
typedef struct _POOLTABLE {
ULONG cbSize; /* size of the pool table structure */
ULONG cCues; /* count of cues in the list */
} POOLTABLE, FAR *LPPOOLTABLE;
/*////////////////////////////////////////////////////////////////////////////
// Structures for the "wsmp" chunk
////////////////////////////////////////////////////////////////////////////*/
#define F_WSMP_NO_TRUNCATION 0x0001l
#define F_WSMP_NO_COMPRESSION 0x0002l
typedef struct _rwsmp {
ULONG cbSize;
USHORT usUnityNote; /* MIDI Unity Playback Note */
SHORT sFineTune; /* Fine Tune in log tuning */
LONG lAttenuation; /* Overall Attenuation to be applied to data */
ULONG fulOptions; /* Flag options */
ULONG cSampleLoops; /* Count of Sample loops, 0 loops is one shot */
} WSMPL, FAR *LPWSMPL;
/* This loop type is a normal forward playing loop which is continually */
/* played until the envelope reaches an off threshold in the release */
/* portion of the volume envelope */
#define WLOOP_TYPE_FORWARD 0
typedef struct _rloop {
ULONG cbSize;
ULONG ulType; /* Loop Type */
ULONG ulStart; /* Start of loop in samples */
ULONG ulLength; /* Length of loop in samples */
} WLOOP, FAR *LPWLOOP;
#endif /*_INC_DLS1 */

View File

@@ -0,0 +1,130 @@
/*
dls2.h
Description:
Interface defines and structures for the DLS2 extensions of DLS.
Written by Microsoft 1998. Released for public use.
*/
#ifndef _INC_DLS2
#define _INC_DLS2
/*
FOURCC's used in the DLS2 file, in addition to DLS1 chunks
*/
#define FOURCC_RGN2 mmioFOURCC('r','g','n','2')
#define FOURCC_LAR2 mmioFOURCC('l','a','r','2')
#define FOURCC_ART2 mmioFOURCC('a','r','t','2')
#define FOURCC_CDL mmioFOURCC('c','d','l',' ')
#define FOURCC_DLID mmioFOURCC('d','l','i','d')
/*
Articulation connection graph definitions. These are in addition to
the definitions in the DLS1 header.
*/
/* Generic Sources (in addition to DLS1 sources. */
#define CONN_SRC_POLYPRESSURE 0x0007 /* Polyphonic Pressure */
#define CONN_SRC_CHANNELPRESSURE 0x0008 /* Channel Pressure */
#define CONN_SRC_VIBRATO 0x0009 /* Vibrato LFO */
#define CONN_SRC_MONOPRESSURE 0x000a /* MIDI Mono pressure */
/* Midi Controllers */
#define CONN_SRC_CC91 0x00db /* Reverb Send */
#define CONN_SRC_CC93 0x00dd /* Chorus Send */
/* Generic Destinations */
#define CONN_DST_GAIN 0x0001 /* Same as CONN_DST_ ATTENUATION, but more appropriate terminology. */
#define CONN_DST_KEYNUMBER 0x0005 /* Key Number Generator */
/* Audio Channel Output Destinations */
#define CONN_DST_LEFT 0x0010 /* Left Channel Send */
#define CONN_DST_RIGHT 0x0011 /* Right Channel Send */
#define CONN_DST_CENTER 0x0012 /* Center Channel Send */
#define CONN_DST_LEFTREAR 0x0013 /* Left Rear Channel Send */
#define CONN_DST_RIGHTREAR 0x0014 /* Right Rear Channel Send */
#define CONN_DST_LFE_CHANNEL 0x0015 /* LFE Channel Send */
#define CONN_DST_CHORUS 0x0080 /* Chorus Send */
#define CONN_DST_REVERB 0x0081 /* Reverb Send */
/* Vibrato LFO Destinations */
#define CONN_DST_VIB_FREQUENCY 0x0114 /* Vibrato Frequency */
#define CONN_DST_VIB_STARTDELAY 0x0115 /* Vibrato Start Delay */
/* EG1 Destinations */
#define CONN_DST_EG1_DELAYTIME 0x020B /* EG1 Delay Time */
#define CONN_DST_EG1_HOLDTIME 0x020C /* EG1 Hold Time */
#define CONN_DST_EG1_SHUTDOWNTIME 0x020D /* EG1 Shutdown Time */
/* EG2 Destinations */
#define CONN_DST_EG2_DELAYTIME 0x030F /* EG2 Delay Time */
#define CONN_DST_EG2_HOLDTIME 0x0310 /* EG2 Hold Time */
/* Filter Destinations */
#define CONN_DST_FILTER_CUTOFF 0x0500 /* Filter Cutoff Frequency */
#define CONN_DST_FILTER_Q 0x0501 /* Filter Resonance */
/* Transforms */
#define CONN_TRN_CONVEX 0x0002 /* Convex Transform */
#define CONN_TRN_SWITCH 0x0003 /* Switch Transform */
/* Conditional chunk operators */
#define DLS_CDL_AND 0x0001 /* X = X & Y */
#define DLS_CDL_OR 0x0002 /* X = X | Y */
#define DLS_CDL_XOR 0x0003 /* X = X ^ Y */
#define DLS_CDL_ADD 0x0004 /* X = X + Y */
#define DLS_CDL_SUBTRACT 0x0005 /* X = X - Y */
#define DLS_CDL_MULTIPLY 0x0006 /* X = X * Y */
#define DLS_CDL_DIVIDE 0x0007 /* X = X / Y */
#define DLS_CDL_LOGICAL_AND 0x0008 /* X = X && Y */
#define DLS_CDL_LOGICAL_OR 0x0009 /* X = X || Y */
#define DLS_CDL_LT 0x000A /* X = (X < Y) */
#define DLS_CDL_LE 0x000B /* X = (X <= Y) */
#define DLS_CDL_GT 0x000C /* X = (X > Y) */
#define DLS_CDL_GE 0x000D /* X = (X >= Y) */
#define DLS_CDL_EQ 0x000E /* X = (X == Y) */
#define DLS_CDL_NOT 0x000F /* X = !X */
#define DLS_CDL_CONST 0x0010 /* 32-bit constant */
#define DLS_CDL_QUERY 0x0011 /* 32-bit value returned from query */
#define DLS_CDL_QUERYSUPPORTED 0x0012 /* Test to see if query is supported by synth */
/*
Loop and release
*/
#define WLOOP_TYPE_RELEASE 1
/*
WaveLink chunk <wlnk-ck>
*/
#define F_WAVELINK_MULTICHANNEL 0x0002
/*
DLSID queries for <cdl-ck>
*/
DEFINE_GUID(DLSID_GMInHardware, 0x178f2f24, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_GSInHardware, 0x178f2f25, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_XGInHardware, 0x178f2f26, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_SupportsDLS1, 0x178f2f27, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_SupportsDLS2, 0xf14599e5, 0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);
DEFINE_GUID(DLSID_SampleMemorySize, 0x178f2f28, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_ManufacturersID, 0xb03e1181, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
DEFINE_GUID(DLSID_ProductID, 0xb03e1182, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
DEFINE_GUID(DLSID_SamplePlaybackRate, 0x2a91f713, 0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
#endif /* _INC_DLS2 */

View File

@@ -0,0 +1,619 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
instrum.c
Code to load and unload GUS-compatible instrument patches.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "instrum_dls.h"
#include "resample.h"
#include "tables.h"
static void free_instrument(MidInstrument *ip)
{
MidSample *sp;
int i;
if (!ip) return;
for (i=0; i<ip->samples; i++)
{
sp=&(ip->sample[i]);
free(sp->data);
}
free(ip->sample);
free(ip);
}
static void free_bank(MidSong *song, int dr, int b)
{
int i;
MidToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]);
for (i=0; i<128; i++)
if (bank->instrument[i])
{
/* Not that this could ever happen, of course */
if (bank->instrument[i] != MAGIC_LOAD_INSTRUMENT)
free_instrument(bank->instrument[i]);
bank->instrument[i]=0;
}
}
static sint32 convert_envelope_rate(MidSong *song, uint8 rate)
{
sint32 r;
r = 3 - ((rate >> 6) & 0x3);
r *= 3;
r = (sint32) (rate & 0x3f) << r; /* 6.9 fixed point */
/* 15.15 fixed point. */
r = ((r * 44100) / song->rate) * song->control_ratio;
#ifdef FAST_DECAY
return r << 10;
#else
return r << 9;
#endif
}
static sint32 convert_envelope_offset(uint8 offset)
{
/* This is not too good... Can anyone tell me what these values mean?
Are they GUS-style "exponential" volumes? And what does that mean? */
/* 15.15 fixed point */
return offset << (7+15);
}
static sint32 convert_tremolo_sweep(MidSong *song, uint8 sweep)
{
if (!sweep)
return 0;
return
((song->control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) /
(song->rate * sweep);
}
static sint32 convert_vibrato_sweep(MidSong *song, uint8 sweep,
sint32 vib_control_ratio)
{
if (!sweep)
return 0;
return
(sint32) (FSCALE((double) (vib_control_ratio) * SWEEP_TUNING, SWEEP_SHIFT)
/ (double)(song->rate * sweep));
/* this was overflowing with seashore.pat
((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) /
(song->rate * sweep); */
}
static sint32 convert_tremolo_rate(MidSong *song, uint8 rate)
{
return
((SINE_CYCLE_LENGTH * song->control_ratio * rate) << RATE_SHIFT) /
(TREMOLO_RATE_TUNING * song->rate);
}
static sint32 convert_vibrato_rate(MidSong *song, uint8 rate)
{
/* Return a suitable vibrato_control_ratio value */
return
(VIBRATO_RATE_TUNING * song->rate) /
(rate * 2 * MID_VIBRATO_SAMPLE_INCREMENTS);
}
static void reverse_data(sint16 *sp, sint32 ls, sint32 le)
{
sint16 s, *ep=sp+le;
sp+=ls;
le-=ls;
le/=2;
while (le--)
{
s=*sp;
*sp++=*ep;
*ep--=s;
}
}
/*
If panning or note_to_use != -1, it will be used for all samples,
instead of the sample-specific values in the instrument file.
For note_to_use, any value <0 or >127 will be forced to 0.
For other parameters, 1 means yes, 0 means no, other values are
undefined.
TODO: do reverse loops right */
static MidInstrument *load_instrument(MidSong *song, char *name, int percussion,
int panning, int amp, int note_to_use,
int strip_loop, int strip_envelope,
int strip_tail)
{
MidInstrument *ip;
MidSample *sp;
FILE *fp;
char tmp[1024];
int i,j,noluck=0;
static char *patch_ext[] = PATCH_EXT_LIST;
if (!name) return 0;
/* Open patch file */
if ((fp=open_file(name)) == NULL)
{
noluck=1;
/* Try with various extensions */
for (i=0; patch_ext[i]; i++)
{
if (strlen(name)+strlen(patch_ext[i])<1024)
{
strcpy(tmp, name);
strcat(tmp, patch_ext[i]);
if ((fp=open_file(tmp)) != NULL)
{
noluck=0;
break;
}
}
}
}
if (noluck)
{
DEBUG_MSG("Instrument `%s' can't be found.\n", name);
return 0;
}
DEBUG_MSG("Loading instrument %s\n", tmp);
/* Read some headers and do cursory sanity checks. There are loads
of magic offsets. This could be rewritten... */
if ((239 != fread(tmp, 1, 239, fp)) ||
(memcmp(tmp, "GF1PATCH110\0ID#000002", 22) &&
memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) /* don't know what the
differences are */
{
DEBUG_MSG("%s: not an instrument\n", name);
return 0;
}
if (tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers,
0 means 1 */
{
DEBUG_MSG("Can't handle patches with %d instruments\n", tmp[82]);
return 0;
}
if (tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */
{
DEBUG_MSG("Can't handle instruments with %d layers\n", tmp[151]);
return 0;
}
ip=safe_malloc(sizeof(MidInstrument));
ip->samples = tmp[198];
ip->sample = safe_malloc(sizeof(MidSample) * ip->samples);
for (i=0; i<ip->samples; i++)
{
uint8 fractions;
sint32 tmplong;
uint16 tmpshort;
uint8 tmpchar;
#define READ_CHAR(thing) \
if (1 != fread(&tmpchar, 1, 1, fp)) goto fail; \
thing = tmpchar;
#define READ_SHORT(thing) \
if (1 != fread(&tmpshort, 2, 1, fp)) goto fail; \
thing = SWAPLE16(tmpshort);
#define READ_LONG(thing) \
if (1 != fread(&tmplong, 4, 1, fp)) goto fail; \
thing = SWAPLE32(tmplong);
fseek(fp, 7, SEEK_CUR); /* Skip the wave name */
if (1 != fread(&fractions, 1, 1, fp))
{
fail:
DEBUG_MSG("Error reading sample %d\n", i);
for (j=0; j<i; j++)
free(ip->sample[j].data);
free(ip->sample);
free(ip);
return 0;
}
sp=&(ip->sample[i]);
READ_LONG(sp->data_length);
READ_LONG(sp->loop_start);
READ_LONG(sp->loop_end);
READ_SHORT(sp->sample_rate);
READ_LONG(sp->low_freq);
READ_LONG(sp->high_freq);
READ_LONG(sp->root_freq);
sp->low_vel = 0;
sp->high_vel = 127;
fseek(fp, 2, SEEK_CUR); /* Why have a "root frequency" and then
* "tuning"?? */
READ_CHAR(tmp[0]);
if (panning==-1)
sp->panning = (tmp[0] * 8 + 4) & 0x7f;
else
sp->panning=(uint8)(panning & 0x7F);
/* envelope, tremolo, and vibrato */
if (18 != fread(tmp, 1, 18, fp)) goto fail;
if (!tmp[13] || !tmp[14])
{
sp->tremolo_sweep_increment=
sp->tremolo_phase_increment=sp->tremolo_depth=0;
DEBUG_MSG(" * no tremolo\n");
}
else
{
sp->tremolo_sweep_increment=convert_tremolo_sweep(song, tmp[12]);
sp->tremolo_phase_increment=convert_tremolo_rate(song, tmp[13]);
sp->tremolo_depth=tmp[14];
DEBUG_MSG(" * tremolo: sweep %d, phase %d, depth %d\n",
sp->tremolo_sweep_increment, sp->tremolo_phase_increment,
sp->tremolo_depth);
}
if (!tmp[16] || !tmp[17])
{
sp->vibrato_sweep_increment=
sp->vibrato_control_ratio=sp->vibrato_depth=0;
DEBUG_MSG(" * no vibrato\n");
}
else
{
sp->vibrato_control_ratio=convert_vibrato_rate(song, tmp[16]);
sp->vibrato_sweep_increment=
convert_vibrato_sweep(song, tmp[15], sp->vibrato_control_ratio);
sp->vibrato_depth=tmp[17];
DEBUG_MSG(" * vibrato: sweep %d, ctl %d, depth %d\n",
sp->vibrato_sweep_increment, sp->vibrato_control_ratio,
sp->vibrato_depth);
}
READ_CHAR(sp->modes);
fseek(fp, 40, SEEK_CUR); /* skip the useless scale frequency, scale
factor (what's it mean?), and reserved
space */
/* Mark this as a fixed-pitch instrument if such a deed is desired. */
if (note_to_use!=-1)
sp->note_to_use=(uint8)(note_to_use);
else
sp->note_to_use=0;
/* seashore.pat in the Midia patch set has no Sustain. I don't
understand why, and fixing it by adding the Sustain flag to
all looped patches probably breaks something else. We do it
anyway. */
if (sp->modes & MODES_LOOPING)
sp->modes |= MODES_SUSTAIN;
/* Strip any loops and envelopes we're permitted to */
if ((strip_loop==1) &&
(sp->modes & (MODES_SUSTAIN | MODES_LOOPING |
MODES_PINGPONG | MODES_REVERSE)))
{
DEBUG_MSG(" - Removing loop and/or sustain\n");
sp->modes &=~(MODES_SUSTAIN | MODES_LOOPING |
MODES_PINGPONG | MODES_REVERSE);
}
if (strip_envelope==1)
{
if (sp->modes & MODES_ENVELOPE)
DEBUG_MSG(" - Removing envelope\n");
sp->modes &= ~MODES_ENVELOPE;
}
else if (strip_envelope != 0)
{
/* Have to make a guess. */
if (!(sp->modes & (MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE)))
{
/* No loop? Then what's there to sustain? No envelope needed
either... */
sp->modes &= ~(MODES_SUSTAIN|MODES_ENVELOPE);
DEBUG_MSG(" - No loop, removing sustain and envelope\n");
}
else if (!memcmp(tmp, "??????", 6) || tmp[11] >= 100)
{
/* Envelope rates all maxed out? Envelope end at a high "offset"?
That's a weird envelope. Take it out. */
sp->modes &= ~MODES_ENVELOPE;
DEBUG_MSG(" - Weirdness, removing envelope\n");
}
else if (!(sp->modes & MODES_SUSTAIN))
{
/* No sustain? Then no envelope. I don't know if this is
justified, but patches without sustain usually don't need the
envelope either... at least the Gravis ones. They're mostly
drums. I think. */
sp->modes &= ~MODES_ENVELOPE;
DEBUG_MSG(" - No sustain, removing envelope\n");
}
}
for (j=0; j<6; j++)
{
sp->envelope_rate[j]=
convert_envelope_rate(song, tmp[j]);
sp->envelope_offset[j]=
convert_envelope_offset(tmp[6+j]);
}
/* Then read the sample data */
sp->data = safe_malloc(sp->data_length);
if (1 != fread(sp->data, sp->data_length, 1, fp))
goto fail;
if (!(sp->modes & MODES_16BIT)) /* convert to 16-bit data */
{
sint32 i=sp->data_length;
uint8 *cp=(uint8 *)(sp->data);
uint16 *tmp,*new;
tmp=new=safe_malloc(sp->data_length*2);
while (i--)
*tmp++ = (uint16)(*cp++) << 8;
cp=(uint8 *)(sp->data);
sp->data = (sample_t *)new;
free(cp);
sp->data_length *= 2;
sp->loop_start *= 2;
sp->loop_end *= 2;
}
#ifndef LITTLE_ENDIAN
else
/* convert to machine byte order */
{
sint32 i=sp->data_length/2;
sint16 *tmp=(sint16 *)sp->data,s;
while (i--)
{
s=SWAPLE16(*tmp);
*tmp++=s;
}
}
#endif
if (sp->modes & MODES_UNSIGNED) /* convert to signed data */
{
sint32 i=sp->data_length/2;
sint16 *tmp=(sint16 *)sp->data;
while (i--)
*tmp++ ^= 0x8000;
}
/* Reverse reverse loops and pass them off as normal loops */
if (sp->modes & MODES_REVERSE)
{
sint32 t;
/* The GUS apparently plays reverse loops by reversing the
whole sample. We do the same because the GUS does not SUCK. */
DEBUG_MSG("Reverse loop in %s\n", name);
reverse_data((sint16 *)sp->data, 0, sp->data_length/2);
t=sp->loop_start;
sp->loop_start=sp->data_length - sp->loop_end;
sp->loop_end=sp->data_length - t;
sp->modes &= ~MODES_REVERSE;
sp->modes |= MODES_LOOPING; /* just in case */
}
#ifdef ADJUST_SAMPLE_VOLUMES
if (amp!=-1)
sp->volume=(float)((amp) / 100.0);
else
{
/* Try to determine a volume scaling factor for the sample.
This is a very crude adjustment, but things sound more
balanced with it. Still, this should be a runtime option. */
sint32 i=sp->data_length/2;
sint16 maxamp=0,a;
sint16 *tmp=(sint16 *)sp->data;
while (i--)
{
a=*tmp++;
if (a<0) a=-a;
if (a>maxamp)
maxamp=a;
}
sp->volume=(float)(32768.0 / maxamp);
DEBUG_MSG(" * volume comp: %f\n", sp->volume);
}
#else
if (amp!=-1)
sp->volume=(double)(amp) / 100.0;
else
sp->volume=1.0;
#endif
sp->data_length /= 2; /* These are in bytes. Convert into samples. */
sp->loop_start /= 2;
sp->loop_end /= 2;
/* Then fractional samples */
sp->data_length <<= FRACTION_BITS;
sp->loop_start <<= FRACTION_BITS;
sp->loop_end <<= FRACTION_BITS;
/* Adjust for fractional loop points. This is a guess. Does anyone
know what "fractions" really stands for? */
sp->loop_start |=
(fractions & 0x0F) << (FRACTION_BITS-4);
sp->loop_end |=
((fractions>>4) & 0x0F) << (FRACTION_BITS-4);
/* If this instrument will always be played on the same note,
and it's not looped, we can resample it now. */
if (sp->note_to_use && !(sp->modes & MODES_LOOPING))
pre_resample(song, sp);
if (strip_tail==1)
{
/* Let's not really, just say we did. */
DEBUG_MSG(" - Stripping tail\n");
sp->data_length = sp->loop_end;
}
}
fclose(fp);
return ip;
}
static int fill_bank(MidSong *song, int dr, int b)
{
int i, errors=0;
MidToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]);
if (!bank)
{
DEBUG_MSG("Huh. Tried to load instruments in non-existent %s %d\n",
(dr) ? "drumset" : "tone bank", b);
return 0;
}
for (i=0; i<128; i++)
{
if (bank->instrument[i]==MAGIC_LOAD_INSTRUMENT)
{
bank->instrument[i]=load_instrument_dls(song, dr, b, i);
if (bank->instrument[i])
{
continue;
}
if (!(bank->tone[i].name))
{
DEBUG_MSG("No instrument mapped to %s %d, program %d%s\n",
(dr)? "drum set" : "tone bank", b, i,
(b!=0) ? "" : " - this instrument will not be heard");
if (b!=0)
{
/* Mark the corresponding instrument in the default
bank / drumset for loading (if it isn't already) */
if (!dr)
{
if (!(song->tonebank[0]->instrument[i]))
song->tonebank[0]->instrument[i] =
MAGIC_LOAD_INSTRUMENT;
}
else
{
if (!(song->drumset[0]->instrument[i]))
song->drumset[0]->instrument[i] =
MAGIC_LOAD_INSTRUMENT;
}
}
bank->instrument[i] = 0;
errors++;
}
else if (!(bank->instrument[i] =
load_instrument(song,
bank->tone[i].name,
(dr) ? 1 : 0,
bank->tone[i].pan,
bank->tone[i].amp,
(bank->tone[i].note!=-1) ?
bank->tone[i].note :
((dr) ? i : -1),
(bank->tone[i].strip_loop!=-1) ?
bank->tone[i].strip_loop :
((dr) ? 1 : -1),
(bank->tone[i].strip_envelope != -1) ?
bank->tone[i].strip_envelope :
((dr) ? 1 : -1),
bank->tone[i].strip_tail )))
{
DEBUG_MSG("Couldn't load instrument %s (%s %d, program %d)\n",
bank->tone[i].name,
(dr)? "drum set" : "tone bank", b, i);
errors++;
}
}
}
return errors;
}
int load_missing_instruments(MidSong *song)
{
int i=128,errors=0;
while (i--)
{
if (song->tonebank[i])
errors+=fill_bank(song,0,i);
if (song->drumset[i])
errors+=fill_bank(song,1,i);
}
return errors;
}
void free_instruments(MidSong *song)
{
int i=128;
while(i--)
{
if (song->tonebank[i])
free_bank(song, 0, i);
if (song->drumset[i])
free_bank(song, 1, i);
}
}
int set_default_instrument(MidSong *song, char *name)
{
MidInstrument *ip;
if (!(ip=load_instrument(song, name, 0, -1, -1, -1, 0, 0, 0)))
return -1;
song->default_instrument = ip;
song->default_program = SPECIAL_PROGRAM;
return 0;
}

View File

@@ -0,0 +1,41 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
instrum.h
*/
/* Bits in modes: */
#define MODES_16BIT (1<<0)
#define MODES_UNSIGNED (1<<1)
#define MODES_LOOPING (1<<2)
#define MODES_PINGPONG (1<<3)
#define MODES_REVERSE (1<<4)
#define MODES_SUSTAIN (1<<5)
#define MODES_ENVELOPE (1<<6)
/* A hack to delay instrument loading until after reading the
entire MIDI file. */
#define MAGIC_LOAD_INSTRUMENT ((MidInstrument *) (-1))
#define SPECIAL_PROGRAM -1
extern int load_missing_instruments(MidSong *song);
extern void free_instruments(MidSong *song);
extern int set_default_instrument(MidSong *song, char *name);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
instrum.h
*/
extern MidInstrument *load_instrument_dls(MidSong *song, int drum, int bank, int instrument);

View File

@@ -0,0 +1,569 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
Suddenly, you realize that this program is free software; you get
an overwhelming urge to redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program 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 General Public License for more details.
You should have received another copy of the GNU General Public
License along with this program; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
I bet they'll be amazed.
mix.c */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "instrum.h"
#include "playmidi.h"
#include "output.h"
#include "tables.h"
#include "resample.h"
#include "mix.h"
/* Returns 1 if envelope runs out */
int recompute_envelope(MidSong *song, int v)
{
int stage;
stage = song->voice[v].envelope_stage;
if (stage>5)
{
/* Envelope ran out. */
song->voice[v].status = VOICE_FREE;
return 1;
}
if (song->voice[v].sample->modes & MODES_ENVELOPE)
{
if (song->voice[v].status==VOICE_ON || song->voice[v].status==VOICE_SUSTAINED)
{
if (stage>2)
{
/* Freeze envelope until note turns off. Trumpets want this. */
song->voice[v].envelope_increment=0;
return 0;
}
}
}
song->voice[v].envelope_stage=stage+1;
if (song->voice[v].envelope_volume==song->voice[v].sample->envelope_offset[stage])
return recompute_envelope(song, v);
song->voice[v].envelope_target = song->voice[v].sample->envelope_offset[stage];
song->voice[v].envelope_increment = song->voice[v].sample->envelope_rate[stage];
if (song->voice[v].envelope_target < song->voice[v].envelope_volume)
song->voice[v].envelope_increment = -song->voice[v].envelope_increment;
return 0;
}
void apply_envelope_to_amp(MidSong *song, int v)
{
float lamp = song->voice[v].left_amp, ramp;
sint32 la,ra;
if (song->voice[v].panned == PANNED_MYSTERY)
{
ramp = song->voice[v].right_amp;
if (song->voice[v].tremolo_phase_increment)
{
lamp *= song->voice[v].tremolo_volume;
ramp *= song->voice[v].tremolo_volume;
}
if (song->voice[v].sample->modes & MODES_ENVELOPE)
{
lamp *= (float)vol_table[song->voice[v].envelope_volume>>23];
ramp *= (float)vol_table[song->voice[v].envelope_volume>>23];
}
la = (sint32)FSCALE(lamp,AMP_BITS);
if (la>MAX_AMP_VALUE)
la=MAX_AMP_VALUE;
ra = (sint32)FSCALE(ramp,AMP_BITS);
if (ra>MAX_AMP_VALUE)
ra=MAX_AMP_VALUE;
song->voice[v].left_mix = la;
song->voice[v].right_mix = ra;
}
else
{
if (song->voice[v].tremolo_phase_increment)
lamp *= song->voice[v].tremolo_volume;
if (song->voice[v].sample->modes & MODES_ENVELOPE)
lamp *= (float)vol_table[song->voice[v].envelope_volume>>23];
la = (sint32)FSCALE(lamp,AMP_BITS);
if (la>MAX_AMP_VALUE)
la=MAX_AMP_VALUE;
song->voice[v].left_mix = la;
}
}
static int update_envelope(MidSong *song, int v)
{
song->voice[v].envelope_volume += song->voice[v].envelope_increment;
/* Why is there no ^^ operator?? */
if (((song->voice[v].envelope_increment < 0) &&
(song->voice[v].envelope_volume <= song->voice[v].envelope_target)) ||
((song->voice[v].envelope_increment > 0) &&
(song->voice[v].envelope_volume >= song->voice[v].envelope_target)))
{
song->voice[v].envelope_volume = song->voice[v].envelope_target;
if (recompute_envelope(song, v))
return 1;
}
return 0;
}
static void update_tremolo(MidSong *song, int v)
{
sint32 depth = song->voice[v].sample->tremolo_depth << 7;
if (song->voice[v].tremolo_sweep)
{
/* Update sweep position */
song->voice[v].tremolo_sweep_position += song->voice[v].tremolo_sweep;
if (song->voice[v].tremolo_sweep_position >= (1 << SWEEP_SHIFT))
song->voice[v].tremolo_sweep=0; /* Swept to max amplitude */
else
{
/* Need to adjust depth */
depth *= song->voice[v].tremolo_sweep_position;
depth >>= SWEEP_SHIFT;
}
}
song->voice[v].tremolo_phase += song->voice[v].tremolo_phase_increment;
/* if (song->voice[v].tremolo_phase >= (SINE_CYCLE_LENGTH<<RATE_SHIFT))
song->voice[v].tremolo_phase -= SINE_CYCLE_LENGTH<<RATE_SHIFT; */
song->voice[v].tremolo_volume = (float)
(1.0 - FSCALENEG((sine(song->voice[v].tremolo_phase >> RATE_SHIFT) + 1.0)
* depth * TREMOLO_AMPLITUDE_TUNING,
17));
/* I'm not sure about the +1.0 there -- it makes tremoloed voices'
volumes on average the lower the higher the tremolo amplitude. */
}
/* Returns 1 if the note died */
static int update_signal(MidSong *song, int v)
{
if (song->voice[v].envelope_increment && update_envelope(song, v))
return 1;
if (song->voice[v].tremolo_phase_increment)
update_tremolo(song, v);
apply_envelope_to_amp(song, v);
return 0;
}
#define MIXATION(a) *lp++ += (a)*s;
static void mix_mystery_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix,
right=vp->right_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
right = vp->right_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
MIXATION(right);
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
right = vp->right_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(right);
}
return;
}
}
static void mix_center_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
MIXATION(left);
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(left);
}
return;
}
}
static void mix_single_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
lp++;
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
lp++;
}
return;
}
}
static void mix_mono_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
}
return;
}
}
static void mix_mystery(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix,
right = song->voice[v].right_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(right);
}
}
static void mix_center(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(left);
}
}
static void mix_single(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
lp++;
}
}
static void mix_mono(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
}
}
/* Ramp a note out in c samples */
static void ramp_out(MidSong *song, sample_t *sp, sint32 *lp, int v, sint32 c)
{
/* should be final_volume_t, but uint8 gives trouble. */
sint32 left, right, li, ri;
sample_t s=0; /* silly warning about uninitialized s */
/* Fix by James Caldwell */
if ( c == 0 ) c = 1;
left=song->voice[v].left_mix;
li=-(left/c);
if (!li) li=-1;
/* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */
if (!(song->encoding & PE_MONO))
{
if (song->voice[v].panned==PANNED_MYSTERY)
{
right=song->voice[v].right_mix;
ri=-(right/c);
while (c--)
{
left += li;
if (left<0)
left=0;
right += ri;
if (right<0)
right=0;
s=*sp++;
MIXATION(left);
MIXATION(right);
}
}
else if (song->voice[v].panned==PANNED_CENTER)
{
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
MIXATION(left);
MIXATION(left);
}
}
else if (song->voice[v].panned==PANNED_LEFT)
{
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
MIXATION(left);
lp++;
}
}
else if (song->voice[v].panned==PANNED_RIGHT)
{
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
lp++;
MIXATION(left);
}
}
}
else
{
/* Mono output. */
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
MIXATION(left);
}
}
}
/**************** interface function ******************/
void mix_voice(MidSong *song, sint32 *buf, int v, sint32 c)
{
MidVoice *vp = song->voice + v;
sample_t *sp;
if (vp->status==VOICE_DIE)
{
if (c>=MAX_DIE_TIME)
c=MAX_DIE_TIME;
sp=resample_voice(song, v, &c);
ramp_out(song, sp, buf, v, c);
vp->status=VOICE_FREE;
}
else
{
sp=resample_voice(song, v, &c);
if (song->encoding & PE_MONO)
{
/* Mono output. */
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_mono_signal(song, sp, buf, v, c);
else
mix_mono(song, sp, buf, v, c);
}
else
{
if (vp->panned == PANNED_MYSTERY)
{
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_mystery_signal(song, sp, buf, v, c);
else
mix_mystery(song, sp, buf, v, c);
}
else if (vp->panned == PANNED_CENTER)
{
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_center_signal(song, sp, buf, v, c);
else
mix_center(song, sp, buf, v, c);
}
else
{
/* It's either full left or full right. In either case,
every other sample is 0. Just get the offset right: */
if (vp->panned == PANNED_RIGHT) buf++;
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_single_signal(song, sp, buf, v, c);
else
mix_single(song, sp, buf, v, c);
}
}
}
}

View File

@@ -0,0 +1,27 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
In case you haven't heard, this program is free software;
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option)
any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
mix.h
*/
extern void mix_voice(MidSong *song, sint32 *buf, int v, sint32 c);
extern int recompute_envelope(MidSong *song, int v);
extern void apply_envelope_to_amp(MidSong *song, int v);

View File

@@ -0,0 +1,113 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* When a patch file can't be opened, one of these extensions is
appended to the filename and the open is tried again.
*/
#define PATCH_EXT_LIST { ".pat", 0 }
/* Acoustic Grand Piano seems to be the usual default instrument. */
#define DEFAULT_PROGRAM 0
/* 9 here is MIDI channel 10, which is the standard percussion channel.
Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too.
On the other hand, some files know that 16 is not a drum channel and
try to play music on it. This is now a runtime option, so this isn't
a critical choice anymore. */
#define DEFAULT_DRUMCHANNELS ((1<<9) | (1<<15))
/* In percent. */
#define DEFAULT_AMPLIFICATION 70
/* Default polyphony */
#define DEFAULT_VOICES 32
/* 1000 here will give a control ratio of 22:1 with 22 kHz output.
Higher CONTROLS_PER_SECOND values allow more accurate rendering
of envelopes and tremolo. The cost is CPU time. */
#define CONTROLS_PER_SECOND 1000
/* Make envelopes twice as fast. Saves ~20% CPU time (notes decay
faster) and sounds more like a GUS. There is now a command line
option to toggle this as well. */
#define FAST_DECAY
/* How many bits to use for the fractional part of sample positions.
This affects tonal accuracy. The entire position counter must fit
in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of
a sample is 1048576 samples (2 megabytes in memory). The GUS gets
by with just 9 bits and a little help from its friends...
"The GUS does not SUCK!!!" -- a happy user :) */
#define FRACTION_BITS 12
/* For some reason the sample volume is always set to maximum in all
patch files. Define this for a crude adjustment that may help
equalize instrument volumes. */
#define ADJUST_SAMPLE_VOLUMES
/* The number of samples to use for ramping out a dying note. Affects
click removal. */
#define MAX_DIE_TIME 20
/**************************************************************************/
/* Anything below this shouldn't need to be changed unless you're porting
to a new machine with other than 32-bit, big-endian words. */
/**************************************************************************/
/* change FRACTION_BITS above, not these */
#define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS)
#define FRACTION_MASK (~ INTEGER_MASK)
/* This is enforced by some computations that must fit in an int */
#define MAX_CONTROL_RATIO 255
#define MAX_AMPLIFICATION 800
/* The TiMidity configuration file */
#define CONFIG_FILE "timidity.cfg"
/* These affect general volume */
#define GUARD_BITS 3
#define AMP_BITS (15-GUARD_BITS)
#define MAX_AMP_VALUE ((1<<(AMP_BITS+1))-1)
#define FSCALE(a,b) (float)((a) * (double)(1<<(b)))
#define FSCALENEG(a,b) (float)((a) * (1.0L / (double)(1<<(b))))
/* Vibrato and tremolo Choices of the Day */
#define SWEEP_TUNING 38
#define VIBRATO_AMPLITUDE_TUNING 1.0L
#define VIBRATO_RATE_TUNING 38
#define TREMOLO_AMPLITUDE_TUNING 1.0L
#define TREMOLO_RATE_TUNING 38
#define SWEEP_SHIFT 16
#define RATE_SHIFT 5
#ifndef PI
#define PI 3.14159265358979323846
#endif
/* The path separator (D.M.) */
#ifdef WIN32
# define PATH_SEP '\\'
#else
# define PATH_SEP '/'
#endif

View File

@@ -0,0 +1,113 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
output.c
Audio output (to file / device) functions.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "output.h"
/*****************************************************************/
/* Some functions to convert signed 32-bit data to other formats */
void s32tos8(void *dp, sint32 *lp, sint32 c)
{
sint8 *cp=(sint8 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-8-GUARD_BITS);
if (l>127) l=127;
else if (l<-128) l=-128;
*cp++ = (sint8) (l);
}
}
void s32tou8(void *dp, sint32 *lp, sint32 c)
{
uint8 *cp=(uint8 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-8-GUARD_BITS);
if (l>127) l=127;
else if (l<-128) l=-128;
*cp++ = 0x80 ^ ((uint8) l);
}
}
void s32tos16(void *dp, sint32 *lp, sint32 c)
{
sint16 *sp=(sint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = (sint16)(l);
}
}
void s32tou16(void *dp, sint32 *lp, sint32 c)
{
uint16 *sp=(uint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = 0x8000 ^ (uint16)(l);
}
}
void s32tos16x(void *dp, sint32 *lp, sint32 c)
{
sint16 *sp=(sint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = XCHG_SHORT((sint16)(l));
}
}
void s32tou16x(void *dp, sint32 *lp, sint32 c)
{
uint16 *sp=(uint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = XCHG_SHORT(0x8000 ^ (uint16)(l));
}
}

View File

@@ -0,0 +1,56 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
output.h
*/
/* Data format encoding bits */
#define PE_MONO 0x01 /* versus stereo */
#define PE_SIGNED 0x02 /* versus unsigned */
#define PE_16BIT 0x04 /* versus 8-bit */
/* Conversion functions -- These overwrite the sint32 data in *lp with
data in another format */
/* 8-bit signed and unsigned*/
extern void s32tos8(void *dp, sint32 *lp, sint32 c);
extern void s32tou8(void *dp, sint32 *lp, sint32 c);
/* 16-bit */
extern void s32tos16(void *dp, sint32 *lp, sint32 c);
extern void s32tou16(void *dp, sint32 *lp, sint32 c);
/* byte-exchanged 16-bit */
extern void s32tos16x(void *dp, sint32 *lp, sint32 c);
extern void s32tou16x(void *dp, sint32 *lp, sint32 c);
/* little-endian and big-endian specific */
#ifdef LITTLE_ENDIAN
#define s32tou16l s32tou16
#define s32tou16b s32tou16x
#define s32tos16l s32tos16
#define s32tos16b s32tos16x
#else
#define s32tou16l s32tou16x
#define s32tou16b s32tou16
#define s32tos16l s32tos16x
#define s32tos16b s32tos16
#endif

View File

@@ -0,0 +1,804 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
playmidi.c -- random stuff in need of rearrangement
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "instrum.h"
#include "playmidi.h"
#include "output.h"
#include "mix.h"
#include "tables.h"
static void adjust_amplification(MidSong *song)
{
song->master_volume = (float)(song->amplification) / (float)100.0;
}
static void reset_voices(MidSong *song)
{
int i;
for (i=0; i<MID_MAX_VOICES; i++)
song->voice[i].status=VOICE_FREE;
}
/* Process the Reset All Controllers event */
static void reset_controllers(MidSong *song, int c)
{
song->channel[c].volume=90; /* Some standard says, although the SCC docs say 0. */
song->channel[c].expression=127; /* SCC-1 does this. */
song->channel[c].sustain=0;
song->channel[c].pitchbend=0x2000;
song->channel[c].pitchfactor=0; /* to be computed */
}
static void reset_midi(MidSong *song)
{
int i;
for (i=0; i<16; i++)
{
reset_controllers(song, i);
/* The rest of these are unaffected by the Reset All Controllers event */
song->channel[i].program=song->default_program;
song->channel[i].panning=NO_PANNING;
song->channel[i].pitchsens=2;
song->channel[i].bank=0; /* tone bank or drum set */
}
reset_voices(song);
}
static void select_sample(MidSong *song, int v, MidInstrument *ip, int vel)
{
sint32 f, cdiff, diff;
int s,i;
MidSample *sp, *closest;
s=ip->samples;
sp=ip->sample;
if (s==1)
{
song->voice[v].sample=sp;
return;
}
f=song->voice[v].orig_frequency;
for (i=0; i<s; i++)
{
if (sp->low_vel <= vel && sp->high_vel >= vel &&
sp->low_freq <= f && sp->high_freq >= f)
{
song->voice[v].sample=sp;
return;
}
sp++;
}
/*
No suitable sample found! We'll select the sample whose root
frequency is closest to the one we want. (Actually we should
probably convert the low, high, and root frequencies to MIDI note
values and compare those.) */
cdiff=0x7FFFFFFF;
closest=sp=ip->sample;
for(i=0; i<s; i++)
{
diff=sp->root_freq - f;
if (diff<0) diff=-diff;
if (diff<cdiff)
{
cdiff=diff;
closest=sp;
}
sp++;
}
song->voice[v].sample=closest;
return;
}
static void recompute_freq(MidSong *song, int v)
{
int
sign=(song->voice[v].sample_increment < 0), /* for bidirectional loops */
pb=song->channel[song->voice[v].channel].pitchbend;
double a;
if (!song->voice[v].sample->sample_rate)
return;
if (song->voice[v].vibrato_control_ratio)
{
/* This instrument has vibrato. Invalidate any precomputed
sample_increments. */
int i=MID_VIBRATO_SAMPLE_INCREMENTS;
while (i--)
song->voice[v].vibrato_sample_increment[i]=0;
}
if (pb==0x2000 || pb<0 || pb>0x3FFF)
song->voice[v].frequency = song->voice[v].orig_frequency;
else
{
pb-=0x2000;
if (!(song->channel[song->voice[v].channel].pitchfactor))
{
/* Damn. Somebody bent the pitch. */
sint32 i=pb*song->channel[song->voice[v].channel].pitchsens;
if (pb<0)
i=-i;
song->channel[song->voice[v].channel].pitchfactor=
(float)(bend_fine[(i>>5) & 0xFF] * bend_coarse[i>>13]);
}
if (pb>0)
song->voice[v].frequency=
(sint32)(song->channel[song->voice[v].channel].pitchfactor *
(double)(song->voice[v].orig_frequency));
else
song->voice[v].frequency=
(sint32)((double)(song->voice[v].orig_frequency) /
song->channel[song->voice[v].channel].pitchfactor);
}
a = FSCALE(((double)(song->voice[v].sample->sample_rate) *
(double)(song->voice[v].frequency)) /
((double)(song->voice[v].sample->root_freq) *
(double)(song->rate)),
FRACTION_BITS);
if (sign)
a = -a; /* need to preserve the loop direction */
song->voice[v].sample_increment = (sint32)(a);
}
static void recompute_amp(MidSong *song, int v)
{
sint32 tempamp;
/* TODO: use fscale */
tempamp= (song->voice[v].velocity *
song->channel[song->voice[v].channel].volume *
song->channel[song->voice[v].channel].expression); /* 21 bits */
if (!(song->encoding & PE_MONO))
{
if (song->voice[v].panning > 60 && song->voice[v].panning < 68)
{
song->voice[v].panned=PANNED_CENTER;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
21);
}
else if (song->voice[v].panning<5)
{
song->voice[v].panned = PANNED_LEFT;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
20);
}
else if (song->voice[v].panning>123)
{
song->voice[v].panned = PANNED_RIGHT;
song->voice[v].left_amp= /* left_amp will be used */
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
20);
}
else
{
song->voice[v].panned = PANNED_MYSTERY;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
27);
song->voice[v].right_amp = song->voice[v].left_amp * (song->voice[v].panning);
song->voice[v].left_amp *= (float)(127 - song->voice[v].panning);
}
}
else
{
song->voice[v].panned = PANNED_CENTER;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
21);
}
}
static void start_note(MidSong *song, MidEvent *e, int i)
{
MidInstrument *ip;
int j;
if (ISDRUMCHANNEL(song, e->channel))
{
if (!(ip=song->drumset[song->channel[e->channel].bank]->instrument[e->a]))
{
if (!(ip=song->drumset[0]->instrument[e->a]))
return; /* No instrument? Then we can't play. */
}
if (ip->samples != 1)
{
DEBUG_MSG("Strange: percussion instrument with %d samples!\n",
ip->samples);
}
if (ip->sample->note_to_use) /* Do we have a fixed pitch? */
song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
else
song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
/* drums are supposed to have only one sample */
song->voice[i].sample = ip->sample;
}
else
{
if (song->channel[e->channel].program == SPECIAL_PROGRAM)
ip=song->default_instrument;
else if (!(ip=song->tonebank[song->channel[e->channel].bank]->
instrument[song->channel[e->channel].program]))
{
if (!(ip=song->tonebank[0]->instrument[song->channel[e->channel].program]))
return; /* No instrument? Then we can't play. */
}
if (ip->sample->note_to_use) /* Fixed-pitch instrument? */
song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
else
song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
select_sample(song, i, ip, e->b);
}
song->voice[i].status = VOICE_ON;
song->voice[i].channel = e->channel;
song->voice[i].note = e->a;
song->voice[i].velocity = e->b;
song->voice[i].sample_offset = 0;
song->voice[i].sample_increment = 0; /* make sure it isn't negative */
song->voice[i].tremolo_phase = 0;
song->voice[i].tremolo_phase_increment = song->voice[i].sample->tremolo_phase_increment;
song->voice[i].tremolo_sweep = song->voice[i].sample->tremolo_sweep_increment;
song->voice[i].tremolo_sweep_position = 0;
song->voice[i].vibrato_sweep = song->voice[i].sample->vibrato_sweep_increment;
song->voice[i].vibrato_sweep_position = 0;
song->voice[i].vibrato_control_ratio = song->voice[i].sample->vibrato_control_ratio;
song->voice[i].vibrato_control_counter = song->voice[i].vibrato_phase = 0;
for (j=0; j<MID_VIBRATO_SAMPLE_INCREMENTS; j++)
song->voice[i].vibrato_sample_increment[j] = 0;
if (song->channel[e->channel].panning != NO_PANNING)
song->voice[i].panning = song->channel[e->channel].panning;
else
song->voice[i].panning = song->voice[i].sample->panning;
recompute_freq(song, i);
recompute_amp(song, i);
if (song->voice[i].sample->modes & MODES_ENVELOPE)
{
/* Ramp up from 0 */
song->voice[i].envelope_stage = 0;
song->voice[i].envelope_volume = 0;
song->voice[i].control_counter = 0;
recompute_envelope(song, i);
apply_envelope_to_amp(song, i);
}
else
{
song->voice[i].envelope_increment = 0;
apply_envelope_to_amp(song, i);
}
}
static void kill_note(MidSong *song, int i)
{
song->voice[i].status = VOICE_DIE;
}
/* Only one instance of a note can be playing on a single channel. */
static void note_on(MidSong *song)
{
int i = song->voices, lowest=-1;
sint32 lv=0x7FFFFFFF, v;
MidEvent *e = song->current_event;
while (i--)
{
if (song->voice[i].status == VOICE_FREE)
lowest=i; /* Can't get a lower volume than silence */
else if (song->voice[i].channel==e->channel &&
(song->voice[i].note==e->a || song->channel[song->voice[i].channel].mono))
kill_note(song, i);
}
if (lowest != -1)
{
/* Found a free voice. */
start_note(song,e,lowest);
return;
}
/* Look for the decaying note with the lowest volume */
i = song->voices;
while (i--)
{
if ((song->voice[i].status != VOICE_ON) &&
(song->voice[i].status != VOICE_DIE))
{
v = song->voice[i].left_mix;
if ((song->voice[i].panned == PANNED_MYSTERY)
&& (song->voice[i].right_mix > v))
v = song->voice[i].right_mix;
if (v<lv)
{
lv=v;
lowest=i;
}
}
}
if (lowest != -1)
{
/* This can still cause a click, but if we had a free voice to
spare for ramping down this note, we wouldn't need to kill it
in the first place... Still, this needs to be fixed. Perhaps
we could use a reserve of voices to play dying notes only. */
song->cut_notes++;
song->voice[lowest].status=VOICE_FREE;
start_note(song,e,lowest);
}
else
song->lost_notes++;
}
static void finish_note(MidSong *song, int i)
{
if (song->voice[i].sample->modes & MODES_ENVELOPE)
{
/* We need to get the envelope out of Sustain stage */
song->voice[i].envelope_stage = 3;
song->voice[i].status = VOICE_OFF;
recompute_envelope(song, i);
apply_envelope_to_amp(song, i);
}
else
{
/* Set status to OFF so resample_voice() will let this voice out
of its loop, if any. In any case, this voice dies when it
hits the end of its data (ofs>=data_length). */
song->voice[i].status = VOICE_OFF;
}
}
static void note_off(MidSong *song)
{
int i = song->voices;
MidEvent *e = song->current_event;
while (i--)
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == e->channel &&
song->voice[i].note == e->a)
{
if (song->channel[e->channel].sustain)
{
song->voice[i].status = VOICE_SUSTAINED;
}
else
finish_note(song, i);
return;
}
}
/* Process the All Notes Off event */
static void all_notes_off(MidSong *song)
{
int i = song->voices;
int c = song->current_event->channel;
DEBUG_MSG("All notes off on channel %d\n", c);
while (i--)
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == c)
{
if (song->channel[c].sustain)
song->voice[i].status = VOICE_SUSTAINED;
else
finish_note(song, i);
}
}
/* Process the All Sounds Off event */
static void all_sounds_off(MidSong *song)
{
int i = song->voices;
int c = song->current_event->channel;
while (i--)
if (song->voice[i].channel == c &&
song->voice[i].status != VOICE_FREE &&
song->voice[i].status != VOICE_DIE)
{
kill_note(song, i);
}
}
static void adjust_pressure(MidSong *song)
{
MidEvent *e = song->current_event;
int i = song->voices;
while (i--)
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == e->channel &&
song->voice[i].note == e->a)
{
song->voice[i].velocity = e->b;
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
return;
}
}
static void drop_sustain(MidSong *song)
{
int i = song->voices;
int c = song->current_event->channel;
while (i--)
if (song->voice[i].status == VOICE_SUSTAINED && song->voice[i].channel == c)
finish_note(song, i);
}
static void adjust_pitchbend(MidSong *song)
{
int c = song->current_event->channel;
int i = song->voices;
while (i--)
if (song->voice[i].status != VOICE_FREE && song->voice[i].channel == c)
{
recompute_freq(song, i);
}
}
static void adjust_volume(MidSong *song)
{
int c = song->current_event->channel;
int i = song->voices;
while (i--)
if (song->voice[i].channel == c &&
(song->voice[i].status==VOICE_ON || song->voice[i].status==VOICE_SUSTAINED))
{
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
}
}
static void seek_forward(MidSong *song, sint32 until_time)
{
reset_voices(song);
while (song->current_event->time < until_time)
{
switch(song->current_event->type)
{
/* All notes stay off. Just handle the parameter changes. */
case ME_PITCH_SENS:
song->channel[song->current_event->channel].pitchsens =
song->current_event->a;
song->channel[song->current_event->channel].pitchfactor = 0;
break;
case ME_PITCHWHEEL:
song->channel[song->current_event->channel].pitchbend =
song->current_event->a + song->current_event->b * 128;
song->channel[song->current_event->channel].pitchfactor = 0;
break;
case ME_MAINVOLUME:
song->channel[song->current_event->channel].volume =
song->current_event->a;
break;
case ME_PAN:
song->channel[song->current_event->channel].panning =
song->current_event->a;
break;
case ME_EXPRESSION:
song->channel[song->current_event->channel].expression =
song->current_event->a;
break;
case ME_PROGRAM:
if (ISDRUMCHANNEL(song, song->current_event->channel))
/* Change drum set */
song->channel[song->current_event->channel].bank =
song->current_event->a;
else
song->channel[song->current_event->channel].program =
song->current_event->a;
break;
case ME_SUSTAIN:
song->channel[song->current_event->channel].sustain =
song->current_event->a;
break;
case ME_RESET_CONTROLLERS:
reset_controllers(song, song->current_event->channel);
break;
case ME_TONE_BANK:
song->channel[song->current_event->channel].bank =
song->current_event->a;
break;
case ME_EOT:
song->current_sample = song->current_event->time;
return;
}
song->current_event++;
}
/*song->current_sample=song->current_event->time;*/
if (song->current_event != song->events)
song->current_event--;
song->current_sample=until_time;
}
static void skip_to(MidSong *song, sint32 until_time)
{
if (song->current_sample > until_time)
song->current_sample = 0;
reset_midi(song);
song->current_event = song->events;
if (until_time)
seek_forward(song, until_time);
}
static void do_compute_data(MidSong *song, sint32 count)
{
int i;
memset(song->common_buffer, 0,
(song->encoding & PE_MONO) ? (count * 4) : (count * 8));
for (i = 0; i < song->voices; i++)
{
if(song->voice[i].status != VOICE_FREE)
mix_voice(song, song->common_buffer, i, count);
}
song->current_sample += count;
}
/* count=0 means flush remaining buffered data to output device, then
flush the device itself */
static void compute_data(MidSong *song, sint8 **stream, sint32 count)
{
int channels;
if ( song->encoding & PE_MONO )
channels = 1;
else
channels = 2;
while (count)
{
sint32 block = count;
if (block > song->buffer_size)
block = song->buffer_size;
do_compute_data(song, block);
song->write(*stream, song->common_buffer, channels * block);
*stream += song->bytes_per_sample * block;
count -= block;
}
}
void mid_song_start(MidSong *song)
{
song->playing = 1;
adjust_amplification(song);
skip_to(song, 0);
}
void mid_song_seek(MidSong *song, uint32 ms)
{
skip_to(song, (ms * (song->rate / 100)) / 10);
}
uint32 mid_song_get_total_time(MidSong *song)
{
MidEvent *last_event = &song->events[song->groomed_event_count - 1];
/* We want last_event->time * 1000 / song->rate */
uint32 retvalue = (last_event->time / song->rate) * 1000;
retvalue += (last_event->time % song->rate) * 1000 / song->rate;
return retvalue;
}
uint32 mid_song_get_time(MidSong *song)
{
uint32 retvalue = (song->current_sample / song->rate) * 1000;
retvalue += (song->current_sample % song->rate) * 1000 / song->rate;
return retvalue;
}
char *mid_song_get_meta(MidSong *song, MidSongMetaId what)
{
return song->meta_data[what];
}
size_t mid_song_read_wave(MidSong *song, void *ptr, size_t size)
{
sint32 start_sample, end_sample, samples;
if (!song->playing)
return 0;
samples = size / song->bytes_per_sample;
start_sample = song->current_sample;
end_sample = song->current_sample+samples;
while ( song->current_sample < end_sample ) {
/* Handle all events that should happen at this time */
while (song->current_event->time <= song->current_sample) {
switch(song->current_event->type) {
/* Effects affecting a single note */
case ME_NOTEON:
if (!(song->current_event->b)) /* Velocity 0? */
note_off(song);
else
note_on(song);
break;
case ME_NOTEOFF:
note_off(song);
break;
case ME_KEYPRESSURE:
adjust_pressure(song);
break;
/* Effects affecting a single channel */
case ME_PITCH_SENS:
song->channel[song->current_event->channel].pitchsens =
song->current_event->a;
song->channel[song->current_event->channel].pitchfactor = 0;
break;
case ME_PITCHWHEEL:
song->channel[song->current_event->channel].pitchbend =
song->current_event->a + song->current_event->b * 128;
song->channel[song->current_event->channel].pitchfactor = 0;
/* Adjust pitch for notes already playing */
adjust_pitchbend(song);
break;
case ME_MAINVOLUME:
song->channel[song->current_event->channel].volume =
song->current_event->a;
adjust_volume(song);
break;
case ME_PAN:
song->channel[song->current_event->channel].panning =
song->current_event->a;
break;
case ME_EXPRESSION:
song->channel[song->current_event->channel].expression =
song->current_event->a;
adjust_volume(song);
break;
case ME_PROGRAM:
if (ISDRUMCHANNEL(song, song->current_event->channel)) {
/* Change drum set */
song->channel[song->current_event->channel].bank =
song->current_event->a;
}
else
song->channel[song->current_event->channel].program =
song->current_event->a;
break;
case ME_SUSTAIN:
song->channel[song->current_event->channel].sustain =
song->current_event->a;
if (!song->current_event->a)
drop_sustain(song);
break;
case ME_RESET_CONTROLLERS:
reset_controllers(song, song->current_event->channel);
break;
case ME_ALL_NOTES_OFF:
all_notes_off(song);
break;
case ME_ALL_SOUNDS_OFF:
all_sounds_off(song);
break;
case ME_TONE_BANK:
song->channel[song->current_event->channel].bank =
song->current_event->a;
break;
case ME_EOT:
/* Give the last notes a couple of seconds to decay */
DEBUG_MSG("Playing time: ~%d seconds\n",
song->current_sample/song->rate+2);
DEBUG_MSG("Notes cut: %d\n", song->cut_notes);
DEBUG_MSG("Notes lost totally: %d\n", song->lost_notes);
song->playing = 0;
return (song->current_sample - start_sample) * song->bytes_per_sample;
}
song->current_event++;
}
if (song->current_event->time > end_sample)
compute_data(song, (sint8 **)&ptr, end_sample-song->current_sample);
else
compute_data(song, (sint8 **)&ptr, song->current_event->time-song->current_sample);
}
return samples * song->bytes_per_sample;
}
void mid_song_set_volume(MidSong *song, int volume)
{
int i;
if (volume > MAX_AMPLIFICATION)
song->amplification = MAX_AMPLIFICATION;
else
if (volume < 0)
song->amplification = 0;
else
song->amplification = volume;
adjust_amplification(song);
for (i = 0; i < song->voices; i++)
if (song->voice[i].status != VOICE_FREE)
{
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
}
}

View File

@@ -0,0 +1,64 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
playmidi.h
*/
/* Midi events */
#define ME_NONE 0
#define ME_NOTEON 1
#define ME_NOTEOFF 2
#define ME_KEYPRESSURE 3
#define ME_MAINVOLUME 4
#define ME_PAN 5
#define ME_SUSTAIN 6
#define ME_EXPRESSION 7
#define ME_PITCHWHEEL 8
#define ME_PROGRAM 9
#define ME_TEMPO 10
#define ME_PITCH_SENS 11
#define ME_ALL_SOUNDS_OFF 12
#define ME_RESET_CONTROLLERS 13
#define ME_ALL_NOTES_OFF 14
#define ME_TONE_BANK 15
#define ME_LYRIC 16
#define ME_EOT 99
/* Causes the instrument's default panning to be used. */
#define NO_PANNING -1
/* Voice status options: */
#define VOICE_FREE 0
#define VOICE_ON 1
#define VOICE_SUSTAINED 2
#define VOICE_OFF 3
#define VOICE_DIE 4
/* Voice panned options: */
#define PANNED_MYSTERY 0
#define PANNED_LEFT 1
#define PANNED_RIGHT 2
#define PANNED_CENTER 3
/* Anything but PANNED_MYSTERY only uses the left volume */
#define ISDRUMCHANNEL(s, c) (((s)->drumchannels & (1<<(c))))

View File

@@ -0,0 +1,596 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
/* Computes how many (fractional) samples one MIDI delta-time unit contains */
static void compute_sample_increment(MidSong *song, sint32 tempo,
sint32 divisions)
{
double a;
a = (double) (tempo) * (double) (song->rate) * (65536.0/1000000.0) /
(double)(divisions);
song->sample_correction = (sint32)(a) & 0xFFFF;
song->sample_increment = (sint32)(a) >> 16;
DEBUG_MSG("Samples per delta-t: %d (correction %d)\n",
song->sample_increment, song->sample_correction);
}
/* Read variable-length number (7 bits per byte, MSB first) */
static sint32 getvl(MidIStream *stream)
{
sint32 l=0;
uint8 c;
for (;;)
{
mid_istream_read(stream, &c, 1, 1);
l += (c & 0x7f);
if (!(c & 0x80)) return l;
l<<=7;
}
}
/* Print a string from the file, followed by a newline. Any non-ASCII
or unprintable characters will be converted to periods. */
static int read_meta_data(MidIStream *stream, sint32 len, uint8 type, MidSong *song)
{
char *s=safe_malloc(len+1);
MidSongMetaId id;
static char *label[] = {
"Text event: ", "Text: ", "Copyright: ", "Track name: ",
"Instrument: ", "Lyric: ", "Marker: ", "Cue point: "};
if (len != (sint32) mid_istream_read(stream, s, 1, len))
{
free(s);
return -1;
}
s[len]='\0';
while (len--)
{
if (((unsigned char)s[len])<32)
s[len]='.';
}
DEBUG_MSG("%s%s\n", label[(type > 7) ? 0 : type], s);
switch (type)
{
case 1: id = MID_SONG_TEXT; break;
case 2: id = MID_SONG_COPYRIGHT; break;
default: free(s); s = NULL;
}
if (s)
{
if (song->meta_data[id])
free(song->meta_data[id]);
song->meta_data[id] = s;
}
return 0;
}
#define MIDIEVENT(at,t,ch,pa,pb) \
new=safe_malloc(sizeof(MidEventList)); \
new->event.time=at; new->event.type=t; new->event.channel=ch; \
new->event.a=pa; new->event.b=pb; new->next=0;\
return new;
#define MAGIC_EOT ((MidEventList *)(-1))
/* Read a MIDI event, returning a freshly allocated element that can
be linked to the event list */
static MidEventList *read_midi_event(MidIStream *stream, MidSong *song)
{
static uint8 laststatus, lastchan;
static uint8 nrpn=0, rpn_msb[16], rpn_lsb[16]; /* one per channel */
uint8 me, type, a,b,c;
sint32 len;
MidEventList *new;
for (;;)
{
song->at += getvl(stream);
if (mid_istream_read(stream, &me, 1, 1) != 1)
{
DEBUG_MSG("read_midi_event: mid_istream_read() failure\n");
return 0;
}
if(me==0xF0 || me == 0xF7) /* SysEx event */
{
len=getvl(stream);
mid_istream_skip(stream, len);
}
else if(me==0xFF) /* Meta event */
{
mid_istream_read(stream, &type, 1, 1);
len=getvl(stream);
if (type>0 && type<16)
{
read_meta_data(stream, len, type, song);
}
else
switch(type)
{
case 0x2F: /* End of Track */
return MAGIC_EOT;
case 0x51: /* Tempo */
mid_istream_read(stream, &a, 1, 1);
mid_istream_read(stream, &b, 1, 1);
mid_istream_read(stream, &c, 1, 1);
MIDIEVENT(song->at, ME_TEMPO, c, a, b);
default:
DEBUG_MSG("(Meta event type 0x%02x, length %d)\n", type, len);
mid_istream_skip(stream, len);
break;
}
}
else
{
a=me;
if (a & 0x80) /* status byte */
{
lastchan=a & 0x0F;
laststatus=(a>>4) & 0x07;
mid_istream_read(stream, &a, 1, 1);
a &= 0x7F;
}
switch(laststatus)
{
case 0: /* Note off */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_NOTEOFF, lastchan, a,b);
case 1: /* Note on */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_NOTEON, lastchan, a,b);
case 2: /* Key Pressure */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_KEYPRESSURE, lastchan, a, b);
case 3: /* Control change */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
{
int control=255;
switch(a)
{
case 7: control=ME_MAINVOLUME; break;
case 10: control=ME_PAN; break;
case 11: control=ME_EXPRESSION; break;
case 64: control=ME_SUSTAIN; break;
case 120: control=ME_ALL_SOUNDS_OFF; break;
case 121: control=ME_RESET_CONTROLLERS; break;
case 123: control=ME_ALL_NOTES_OFF; break;
/* These should be the SCC-1 tone bank switch
commands. I don't know why there are two, or
why the latter only allows switching to bank 0.
Also, some MIDI files use 0 as some sort of
continuous controller. This will cause lots of
warnings about undefined tone banks. */
case 0: control=ME_TONE_BANK; break;
case 32:
if (b!=0)
DEBUG_MSG("(Strange: tone bank change 0x20%02x)\n", b);
else
control=ME_TONE_BANK;
break;
case 100: nrpn=0; rpn_msb[lastchan]=b; break;
case 101: nrpn=0; rpn_lsb[lastchan]=b; break;
case 99: nrpn=1; rpn_msb[lastchan]=b; break;
case 98: nrpn=1; rpn_lsb[lastchan]=b; break;
case 6:
if (nrpn)
{
DEBUG_MSG("(Data entry (MSB) for NRPN %02x,%02x: %d)\n",
rpn_msb[lastchan], rpn_lsb[lastchan], b);
break;
}
switch((rpn_msb[lastchan]<<8) | rpn_lsb[lastchan])
{
case 0x0000: /* Pitch bend sensitivity */
control=ME_PITCH_SENS;
break;
case 0x7F7F: /* RPN reset */
/* reset pitch bend sensitivity to 2 */
MIDIEVENT(song->at, ME_PITCH_SENS, lastchan, 2, 0);
default:
DEBUG_MSG("(Data entry (MSB) for RPN %02x,%02x: %d)\n",
rpn_msb[lastchan], rpn_lsb[lastchan], b);
break;
}
break;
default:
DEBUG_MSG("(Control %d: %d)\n", a, b);
break;
}
if (control != 255)
{
MIDIEVENT(song->at, control, lastchan, b, 0);
}
}
break;
case 4: /* Program change */
a &= 0x7f;
MIDIEVENT(song->at, ME_PROGRAM, lastchan, a, 0);
case 5: /* Channel pressure - NOT IMPLEMENTED */
break;
case 6: /* Pitch wheel */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_PITCHWHEEL, lastchan, a, b);
default:
DEBUG_MSG("*** Can't happen: status 0x%02X, channel 0x%02X\n",
laststatus, lastchan);
break;
}
}
}
return new;
}
#undef MIDIEVENT
/* Read a midi track into the linked list, either merging with any previous
tracks or appending to them. */
static int read_track(MidIStream *stream, MidSong *song, int append)
{
MidEventList *meep;
MidEventList *next, *new;
sint32 len;
char tmp[4];
meep = song->evlist;
if (append && meep)
{
/* find the last event in the list */
for (; meep->next; meep=meep->next)
;
song->at = meep->event.time;
}
else
song->at=0;
/* Check the formalities */
if (mid_istream_read(stream, tmp, 1, 4) != 4 || mid_istream_read(stream, &len, 4, 1) != 1)
{
DEBUG_MSG("Can't read track header.\n");
return -1;
}
len=SWAPBE32(len);
if (memcmp(tmp, "MTrk", 4))
{
DEBUG_MSG("Corrupt MIDI file.\n");
return -2;
}
for (;;)
{
if (!(new=read_midi_event(stream, song))) /* Some kind of error */
return -2;
if (new==MAGIC_EOT) /* End-of-track Hack. */
{
return 0;
}
next=meep->next;
while (next && (next->event.time < new->event.time))
{
meep=next;
next=meep->next;
}
new->next=next;
meep->next=new;
song->event_count++; /* Count the event. (About one?) */
meep=new;
}
}
/* Free the linked event list from memory. */
static void free_midi_list(MidSong *song)
{
MidEventList *meep, *next;
if (!(meep = song->evlist)) return;
while (meep)
{
next=meep->next;
free(meep);
meep=next;
}
song->evlist=0;
}
/* Allocate an array of MidiEvents and fill it from the linked list of
events, marking used instruments for loading. Convert event times to
samples: handle tempo changes. Strip unnecessary events from the list.
Free the linked list. */
static MidEvent *groom_list(MidSong *song, sint32 divisions,sint32 *eventsp,
sint32 *samplesp)
{
MidEvent *groomed_list, *lp;
MidEventList *meep;
sint32 i, our_event_count, tempo, skip_this_event, new_value;
sint32 sample_cum, samples_to_do, at, st, dt, counting_time;
int current_bank[16], current_set[16], current_program[16];
/* Or should each bank have its own current program? */
for (i=0; i<16; i++)
{
current_bank[i]=0;
current_set[i]=0;
current_program[i]=song->default_program;
}
tempo=500000;
compute_sample_increment(song, tempo, divisions);
/* This may allocate a bit more than we need */
groomed_list=lp=safe_malloc(sizeof(MidEvent) * (song->event_count+1));
meep=song->evlist;
our_event_count=0;
st=at=sample_cum=0;
counting_time=2; /* We strip any silence before the first NOTE ON. */
for (i = 0; i < song->event_count; i++)
{
skip_this_event=0;
if (meep->event.type==ME_TEMPO)
{
tempo=
meep->event.channel + meep->event.b * 256 + meep->event.a * 65536;
compute_sample_increment(song, tempo, divisions);
skip_this_event=1;
}
else switch (meep->event.type)
{
case ME_PROGRAM:
if (ISDRUMCHANNEL(song, meep->event.channel))
{
if (song->drumset[meep->event.a]) /* Is this a defined drumset? */
new_value=meep->event.a;
else
{
DEBUG_MSG("Drum set %d is undefined\n", meep->event.a);
new_value=meep->event.a=0;
}
if (current_set[meep->event.channel] != new_value)
current_set[meep->event.channel]=new_value;
else
skip_this_event=1;
}
else
{
new_value=meep->event.a;
if ((current_program[meep->event.channel] != SPECIAL_PROGRAM)
&& (current_program[meep->event.channel] != new_value))
current_program[meep->event.channel] = new_value;
else
skip_this_event=1;
}
break;
case ME_NOTEON:
if (counting_time)
counting_time=1;
if (ISDRUMCHANNEL(song, meep->event.channel))
{
/* Mark this instrument to be loaded */
if (!(song->drumset[current_set[meep->event.channel]]
->instrument[meep->event.a]))
song->drumset[current_set[meep->event.channel]]
->instrument[meep->event.a] = MAGIC_LOAD_INSTRUMENT;
}
else
{
if (current_program[meep->event.channel]==SPECIAL_PROGRAM)
break;
/* Mark this instrument to be loaded */
if (!(song->tonebank[current_bank[meep->event.channel]]
->instrument[current_program[meep->event.channel]]))
song->tonebank[current_bank[meep->event.channel]]
->instrument[current_program[meep->event.channel]] =
MAGIC_LOAD_INSTRUMENT;
}
break;
case ME_TONE_BANK:
if (ISDRUMCHANNEL(song, meep->event.channel))
{
skip_this_event=1;
break;
}
if (song->tonebank[meep->event.a]) /* Is this a defined tone bank? */
new_value=meep->event.a;
else
{
DEBUG_MSG("Tone bank %d is undefined\n", meep->event.a);
new_value=meep->event.a=0;
}
if (current_bank[meep->event.channel]!=new_value)
current_bank[meep->event.channel]=new_value;
else
skip_this_event=1;
break;
}
/* Recompute time in samples*/
if ((dt=meep->event.time - at) && !counting_time)
{
samples_to_do = song->sample_increment * dt;
sample_cum += song->sample_correction * dt;
if (sample_cum & 0xFFFF0000)
{
samples_to_do += ((sample_cum >> 16) & 0xFFFF);
sample_cum &= 0x0000FFFF;
}
st += samples_to_do;
}
else if (counting_time==1) counting_time=0;
if (!skip_this_event)
{
/* Add the event to the list */
*lp=meep->event;
lp->time=st;
lp++;
our_event_count++;
}
at=meep->event.time;
meep=meep->next;
}
/* Add an End-of-Track event */
lp->time=st;
lp->type=ME_EOT;
our_event_count++;
free_midi_list(song);
*eventsp=our_event_count;
*samplesp=st;
return groomed_list;
}
MidEvent *read_midi_file(MidIStream *stream, MidSong *song, sint32 *count, sint32 *sp)
{
sint32 len, divisions;
sint16 format, tracks, divisions_tmp;
int i;
char tmp[4];
song->event_count=0;
song->at=0;
song->evlist=0;
if (mid_istream_read(stream, tmp, 1, 4) != 4 || mid_istream_read(stream, &len, 4, 1) != 1)
{
DEBUG_MSG("Not a MIDI file!\n");
return 0;
}
len=SWAPBE32(len);
if (memcmp(tmp, "MThd", 4) || len < 6)
{
DEBUG_MSG("Not a MIDI file!\n");
return 0;
}
mid_istream_read(stream, &format, 2, 1);
mid_istream_read(stream, &tracks, 2, 1);
mid_istream_read(stream, &divisions_tmp, 2, 1);
format=SWAPBE16(format);
tracks=SWAPBE16(tracks);
divisions_tmp=SWAPBE16(divisions_tmp);
if (divisions_tmp<0)
{
/* SMPTE time -- totally untested. Got a MIDI file that uses this? */
divisions=
(sint32)(-(divisions_tmp/256)) * (sint32)(divisions_tmp & 0xFF);
}
else divisions=(sint32)(divisions_tmp);
if (len > 6)
{
DEBUG_MSG("MIDI file header size %u bytes", len);
mid_istream_skip(stream, len-6); /* skip the excess */
}
if (format<0 || format >2)
{
DEBUG_MSG("Unknown MIDI file format %d\n", format);
return 0;
}
DEBUG_MSG("Format: %d Tracks: %d Divisions: %d\n",
format, tracks, divisions);
/* Put a do-nothing event first in the list for easier processing */
song->evlist=safe_malloc(sizeof(MidEventList));
song->evlist->event.time=0;
song->evlist->event.type=ME_NONE;
song->evlist->next=0;
song->event_count++;
switch(format)
{
case 0:
if (read_track(stream, song, 0))
{
free_midi_list(song);
return 0;
}
break;
case 1:
for (i=0; i<tracks; i++)
if (read_track(stream, song, 0))
{
free_midi_list(song);
return 0;
}
break;
case 2: /* We simply play the tracks sequentially */
for (i=0; i<tracks; i++)
if (read_track(stream, song, 1))
{
free_midi_list(song);
return 0;
}
break;
}
return groom_list(song, divisions, count, sp);
}

View File

@@ -0,0 +1,24 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
readmidi.h
*/
extern MidEvent *read_midi_file(MidIStream *stream, MidSong *song, sint32 *count, sint32 *sp);

View File

@@ -0,0 +1,608 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
resample.c
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "tables.h"
#include "resample.h"
/*************** resampling with fixed increment *****************/
static sample_t *rs_plain(MidSong *song, int v, sint32 *countptr)
{
/* Play sample until end, then free the voice. */
sample_t v1, v2;
MidVoice
*vp=&(song->voice[v]);
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->data_length,
count=*countptr;
sint32 i;
if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */
/* Precalc how many times we should go through the loop.
NOTE: Assumes that incr > 0 and that ofs <= le */
i = (le - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (ofs >= le)
{
if (ofs == le)
*dest++ = src[ofs >> FRACTION_BITS];
vp->status=VOICE_FREE;
*countptr-=count+1;
}
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_loop(MidSong *song, MidVoice *vp, sint32 count)
{
/* Play sample until end-of-loop, skip back and continue. */
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ll=le - vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32 i;
while (count)
{
if (ofs >= le)
/* NOTE: Assumes that ll > incr and that incr > 0. */
ofs -= ll;
/* Precalc how many times we should go through the loop */
i = (le - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
}
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_bidir(MidSong *song, MidVoice *vp, sint32 count)
{
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ls=vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32
le2 = le<<1,
ls2 = ls<<1,
i;
/* Play normally until inside the loop region */
if (ofs <= ls)
{
/* NOTE: Assumes that incr > 0, which is NOT always the case
when doing bidirectional looping. I have yet to see a case
where both ofs <= ls AND incr < 0, however. */
i = (ls - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
}
/* Then do the bidirectional looping */
while(count)
{
/* Precalc how many times we should go through the loop */
i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (ofs>=le)
{
/* fold the overshoot back in */
ofs = le2 - ofs;
incr *= -1;
}
else if (ofs <= ls)
{
ofs = ls2 - ofs;
incr *= -1;
}
}
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
/*********************** vibrato versions ***************************/
/* We only need to compute one half of the vibrato sine cycle */
static int vib_phase_to_inc_ptr(int phase)
{
if (phase < MID_VIBRATO_SAMPLE_INCREMENTS/2)
return MID_VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
else if (phase >= 3*MID_VIBRATO_SAMPLE_INCREMENTS/2)
return 5*MID_VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
else
return phase-MID_VIBRATO_SAMPLE_INCREMENTS/2;
}
static sint32 update_vibrato(MidSong *song, MidVoice *vp, int sign)
{
sint32 depth;
int phase, pb;
double a;
if (vp->vibrato_phase++ >= 2*MID_VIBRATO_SAMPLE_INCREMENTS-1)
vp->vibrato_phase=0;
phase=vib_phase_to_inc_ptr(vp->vibrato_phase);
if (vp->vibrato_sample_increment[phase])
{
if (sign)
return -vp->vibrato_sample_increment[phase];
else
return vp->vibrato_sample_increment[phase];
}
/* Need to compute this sample increment. */
depth=vp->sample->vibrato_depth<<7;
if (vp->vibrato_sweep)
{
/* Need to update sweep */
vp->vibrato_sweep_position += vp->vibrato_sweep;
if (vp->vibrato_sweep_position >= (1<<SWEEP_SHIFT))
vp->vibrato_sweep=0;
else
{
/* Adjust depth */
depth *= vp->vibrato_sweep_position;
depth >>= SWEEP_SHIFT;
}
}
a = FSCALE(((double)(vp->sample->sample_rate) *
(double)(vp->frequency)) /
((double)(vp->sample->root_freq) *
(double)(song->rate)),
FRACTION_BITS);
pb=(int)((sine(vp->vibrato_phase *
(SINE_CYCLE_LENGTH/(2*MID_VIBRATO_SAMPLE_INCREMENTS)))
* (double)(depth) * VIBRATO_AMPLITUDE_TUNING));
if (pb<0)
{
pb=-pb;
a /= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13];
}
else
a *= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13];
/* If the sweep's over, we can store the newly computed sample_increment */
if (!vp->vibrato_sweep)
vp->vibrato_sample_increment[phase]=(sint32) a;
if (sign)
a = -a; /* need to preserve the loop direction */
return (sint32) a;
}
static sample_t *rs_vib_plain(MidSong *song, int v, sint32 *countptr)
{
/* Play sample until end, then free the voice. */
sample_t v1, v2;
MidVoice *vp=&(song->voice[v]);
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32
le=vp->sample->data_length,
ofs=vp->sample_offset,
incr=vp->sample_increment,
count=*countptr;
int
cc=vp->vibrato_control_counter;
/* This has never been tested */
if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */
while (count--)
{
if (!cc--)
{
cc=vp->vibrato_control_ratio;
incr=update_vibrato(song, vp, 0);
}
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
if (ofs >= le)
{
if (ofs == le)
*dest++ = src[ofs >> FRACTION_BITS];
vp->status=VOICE_FREE;
*countptr-=count+1;
break;
}
}
vp->vibrato_control_counter=cc;
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_vib_loop(MidSong *song, MidVoice *vp, sint32 count)
{
/* Play sample until end-of-loop, skip back and continue. */
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ll=le - vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
int
cc=vp->vibrato_control_counter;
sint32 i;
int
vibflag=0;
while (count)
{
/* Hopefully the loop is longer than an increment */
if(ofs >= le)
ofs -= ll;
/* Precalc how many times to go through the loop, taking
the vibrato control ratio into account this time. */
i = (le - ofs) / incr + 1;
if(i > count) i = count;
if(i > cc)
{
i = cc;
vibflag = 1;
}
else cc -= i;
count -= i;
while(i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if(vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(song, vp, 0);
vibflag = 0;
}
}
vp->vibrato_control_counter=cc;
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_vib_bidir(MidSong *song, MidVoice *vp, sint32 count)
{
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ls=vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
int
cc=vp->vibrato_control_counter;
sint32
le2=le<<1,
ls2=ls<<1,
i;
int
vibflag = 0;
/* Play normally until inside the loop region */
while (count && (ofs <= ls))
{
i = (ls - ofs) / incr + 1;
if (i > count) i = count;
if (i > cc)
{
i = cc;
vibflag = 1;
}
else cc -= i;
count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(song, vp, 0);
vibflag = 0;
}
}
/* Then do the bidirectional looping */
while (count)
{
/* Precalc how many times we should go through the loop */
i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
if(i > count) i = count;
if(i > cc)
{
i = cc;
vibflag = 1;
}
else cc -= i;
count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(song, vp, (incr < 0));
vibflag = 0;
}
if (ofs >= le)
{
/* fold the overshoot back in */
ofs = le2 - ofs;
incr *= -1;
}
else if (ofs <= ls)
{
ofs = ls2 - ofs;
incr *= -1;
}
}
vp->vibrato_control_counter=cc;
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
sample_t *resample_voice(MidSong *song, int v, sint32 *countptr)
{
sint32 ofs;
uint8 modes;
MidVoice *vp=&(song->voice[v]);
if (!(vp->sample->sample_rate))
{
/* Pre-resampled data -- just update the offset and check if
we're out of data. */
ofs=vp->sample_offset >> FRACTION_BITS; /* Kind of silly to use
FRACTION_BITS here... */
if (*countptr >= (vp->sample->data_length>>FRACTION_BITS) - ofs)
{
/* Note finished. Free the voice. */
vp->status = VOICE_FREE;
/* Let the caller know how much data we had left */
*countptr = (vp->sample->data_length>>FRACTION_BITS) - ofs;
}
else
vp->sample_offset += *countptr << FRACTION_BITS;
return vp->sample->data+ofs;
}
/* Need to resample. Use the proper function. */
modes=vp->sample->modes;
if (vp->vibrato_control_ratio)
{
if ((modes & MODES_LOOPING) &&
((modes & MODES_ENVELOPE) ||
(vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED)))
{
if (modes & MODES_PINGPONG)
return rs_vib_bidir(song, vp, *countptr);
else
return rs_vib_loop(song, vp, *countptr);
}
else
return rs_vib_plain(song, v, countptr);
}
else
{
if ((modes & MODES_LOOPING) &&
((modes & MODES_ENVELOPE) ||
(vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED)))
{
if (modes & MODES_PINGPONG)
return rs_bidir(song, vp, *countptr);
else
return rs_loop(song, vp, *countptr);
}
else
return rs_plain(song, v, countptr);
}
}
void pre_resample(MidSong *song, MidSample *sp)
{
double a, xdiff;
sint32 incr, ofs, newlen, count;
sint16 *newdata, *dest, *src = (sint16 *) sp->data;
sint16 v1, v2, v3, v4, *vptr;
#ifdef DEBUG_CHATTER
static const char note_name[12][3] =
{
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
#endif
DEBUG_MSG(" * pre-resampling for note %d (%s%d)\n",
sp->note_to_use,
note_name[sp->note_to_use % 12], (sp->note_to_use & 0x7F) / 12);
a = ((double) (sp->sample_rate) * freq_table[(int) (sp->note_to_use)]) /
((double) (sp->root_freq) * song->rate);
newlen = (sint32)(sp->data_length / a);
dest = newdata = safe_malloc(newlen >> (FRACTION_BITS - 1));
count = (newlen >> FRACTION_BITS) - 1;
ofs = incr = (sp->data_length - (1 << FRACTION_BITS)) / count;
if (--count)
*dest++ = src[0];
/* Since we're pre-processing and this doesn't have to be done in
real-time, we go ahead and do the full sliding cubic interpolation. */
while (--count)
{
vptr = src + (ofs >> FRACTION_BITS);
/*
* Electric Fence to the rescue: Accessing *(vptr - 1) is not a
* good thing to do when vptr <= src. (TiMidity++ has a similar
* safe-guard here.)
*/
v1 = (vptr > src) ? *(vptr - 1) : 0;
v2 = *vptr;
v3 = *(vptr + 1);
v4 = *(vptr + 2);
xdiff = FSCALENEG(ofs & FRACTION_MASK, FRACTION_BITS);
*dest++ = (sint16)(v2 + (xdiff / 6.0) * (-2 * v1 - 3 * v2 + 6 * v3 - v4 +
xdiff * (3 * (v1 - 2 * v2 + v3) + xdiff * (-v1 + 3 * (v2 - v3) + v4))));
ofs += incr;
}
if (ofs & FRACTION_MASK)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS) + 1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
}
else
*dest++ = src[ofs >> FRACTION_BITS];
sp->data_length = newlen;
sp->loop_start = (sint32)(sp->loop_start / a);
sp->loop_end = (sint32)(sp->loop_end / a);
free(sp->data);
sp->data = (sample_t *) newdata;
sp->sample_rate = 0;
}

View File

@@ -0,0 +1,24 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
resample.h
*/
extern sample_t *resample_voice(MidSong *song, int v, sint32 *countptr);
extern void pre_resample(MidSong *song, MidSample *sp);

View File

@@ -0,0 +1,188 @@
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include "string.h"
#include "timidity.h"
#include "timidity_internal.h"
#include "common.h"
struct _MidIStream
{
MidIStreamReadFunc read;
MidIStreamCloseFunc close;
void *ctx;
};
typedef struct StdIOContext
{
FILE *fp;
int autoclose;
} StdIOContext;
size_t
stdio_istream_read (void *ctx, void *ptr, size_t size, size_t nmemb)
{
return fread (ptr, size, nmemb, ((StdIOContext *) ctx)->fp);
}
int
stdio_istream_close (void *ctx)
{
int ret = 0;
if (((StdIOContext *) ctx)->autoclose)
ret = fclose (((StdIOContext *) ctx)->fp);
free (ctx);
return ret;
}
typedef struct MemContext
{
sint8 *base;
sint8 *current;
sint8 *end;
int autofree;
} MemContext;
size_t
mem_istream_read (void *ctx, void *ptr, size_t size, size_t nmemb)
{
MemContext *c;
size_t count;
c = (MemContext *) ctx;
count = nmemb;
if (c->current + count * size > c->end)
count = (c->end - c->current) / size;
memcpy (ptr, c->current, count * size);
c->current += count * size;
return count;
}
int
mem_istream_close (void *ctx)
{
if (((MemContext *) ctx)->autofree)
free (((MemContext *) ctx)->base);
free (ctx);
return 0;
}
MidIStream *
mid_istream_open_fp (FILE * fp, int autoclose)
{
StdIOContext *ctx;
MidIStream *stream;
stream = safe_malloc (sizeof (MidIStream));
if (stream == NULL)
return NULL;
ctx = safe_malloc (sizeof (StdIOContext));
if (ctx == NULL)
{
free (stream);
return NULL;
}
ctx->fp = fp;
ctx->autoclose = autoclose;
stream->ctx = ctx;
stream->read = stdio_istream_read;
stream->close = stdio_istream_close;
return stream;
}
MidIStream *
mid_istream_open_file (const char *file)
{
FILE *fp;
fp = fopen (file, "rb");
if (fp == NULL)
return NULL;
return mid_istream_open_fp (fp, 1);
}
MidIStream *
mid_istream_open_mem (void *mem, size_t size, int autofree)
{
MemContext *ctx;
MidIStream *stream;
stream = safe_malloc (sizeof (MidIStream));
if (stream == NULL)
return NULL;
ctx = safe_malloc (sizeof (MemContext));
if (ctx == NULL)
{
free (stream);
return NULL;
}
ctx->base = mem;
ctx->current = mem;
ctx->end = ((sint8 *) mem) + size;
ctx->autofree = autofree;
stream->ctx = ctx;
stream->read = mem_istream_read;
stream->close = mem_istream_close;
return stream;
}
MidIStream *
mid_istream_open_callbacks (MidIStreamReadFunc read,
MidIStreamCloseFunc close, void *context)
{
MidIStream *stream;
stream = safe_malloc (sizeof (MidIStream));
if (stream == NULL)
return NULL;
stream->ctx = context;
stream->read = read;
stream->close = close;
return stream;
}
size_t
mid_istream_read (MidIStream * stream, void *ptr, size_t size, size_t nmemb)
{
return stream->read (stream->ctx, ptr, size, nmemb);
}
void
mid_istream_skip (MidIStream * stream, size_t len)
{
size_t c;
char tmp[1024];
while (len > 0)
{
c = len;
if (c > 1024)
c = 1024;
len -= c;
if (c != mid_istream_read (stream, tmp, 1, c))
{
DEBUG_MSG ("mid_istream_skip error\n");
}
}
}
int
mid_istream_close (MidIStream * stream)
{
int ret = stream->close (stream->ctx);
free (stream);
return ret;
}

View File

@@ -0,0 +1,214 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include "timidity.h"
#include "tables.h"
const sint32 freq_table[128]=
{
8176, 8662, 9177, 9723,
10301, 10913, 11562, 12250,
12978, 13750, 14568, 15434,
16352, 17324, 18354, 19445,
20602, 21827, 23125, 24500,
25957, 27500, 29135, 30868,
32703, 34648, 36708, 38891,
41203, 43654, 46249, 48999,
51913, 55000, 58270, 61735,
65406, 69296, 73416, 77782,
82407, 87307, 92499, 97999,
103826, 110000, 116541, 123471,
130813, 138591, 146832, 155563,
164814, 174614, 184997, 195998,
207652, 220000, 233082, 246942,
261626, 277183, 293665, 311127,
329628, 349228, 369994, 391995,
415305, 440000, 466164, 493883,
523251, 554365, 587330, 622254,
659255, 698456, 739989, 783991,
830609, 880000, 932328, 987767,
1046502, 1108731, 1174659, 1244508,
1318510, 1396913, 1479978, 1567982,
1661219, 1760000, 1864655, 1975533,
2093005, 2217461, 2349318, 2489016,
2637020, 2793826, 2959955, 3135963,
3322438, 3520000, 3729310, 3951066,
4186009, 4434922, 4698636, 4978032,
5274041, 5587652, 5919911, 6271927,
6644875, 7040000, 7458620, 7902133,
8372018, 8869844, 9397273, 9956063,
10548082, 11175303, 11839822, 12543854
};
/* v=2.^((x/127-1) * 6) */
const double vol_table[128] =
{
0.015625, 0.016145143728351113, 0.016682602624583379, 0.017237953096759438,
0.017811790741104401, 0.01840473098076444, 0.019017409725829021, 0.019650484055324921,
0.020304632921913132, 0.020980557880044631, 0.021678983838355849, 0.02240065983711079,
0.023146359851523596, 0.023916883621822989, 0.024713057510949051, 0.025535735390801884,
0.026385799557992876, 0.027264161680080529, 0.028171763773305786, 0.029109579212875332,
0.030078613776876421, 0.031079906724942836, 0.032114531912828696, 0.033183598944085631,
0.034288254360078256, 0.035429682869614412, 0.036609108619508737, 0.037827796507442342,
0.039087053538526394, 0.040388230227024875, 0.041732722044739302, 0.043121970917609151,
0.044557466772132896, 0.046040749133268132, 0.047573408775524545, 0.049157089429020417,
0.050793489542332405, 0.05248436410402918, 0.054231526524842463, 0.056036850582493913,
0.057902272431264008, 0.059829792678457581, 0.061821478529993396, 0.063879466007418645,
0.066005962238725971, 0.068203247825430205, 0.070473679288442961, 0.072819691595368496,
0.075243800771931268, 0.077748606600335793, 0.080336795407452768, 0.083011142945821612,
0.085774517370559328, 0.088629882315368294, 0.091580300070941839, 0.094628934869176312,
0.097779056276712184, 0.10103404270144323, 0.1043973850157546, 0.1078726903003755,
0.11146368571286204, 0.11517422248485852, 0.11900828005242428, 0.12296997032385605,
0.12706354208958254, 0.13129338557886089, 0.13566403716816194, 0.14018018424629392,
0.14484667024148207, 0.14966849981579558, 0.15465084423249356, 0.15979904690204472,
0.16511862911277009, 0.17061529595225433, 0.17629494242587571, 0.18216365977901747,
0.18822774202974024, 0.19449369271892172, 0.20096823188510385, 0.20765830327152621,
0.21457108177307616, 0.22171398113114205, 0.2290946618846218, 0.23672103958561411,
0.2446012932886038, 0.25274387432224471, 0.26115751535314891, 0.26985123975140174,
0.27883437126784744, 0.28811654403352405, 0.29770771289197112, 0.30761816407549192,
0.31785852623682015, 0.32843978184802081, 0.33937327897885317, 0.3506707434672246,
0.36234429149478936, 0.37440644258117928, 0.38687013301080181, 0.39974872970660535,
0.41305604456569134, 0.42680634927214656, 0.44101439060298442, 0.45569540624360722,
0.47086514112975281, 0.48653986433345225, 0.50273638651110641, 0.51947207793239625,
0.53676488710936021, 0.55463336004561792, 0.57309666012638816, 0.59217458867062556,
0.61188760616732485, 0.63225685421876243, 0.65330417821421161, 0.67505215075844849,
0.69752409588017272, 0.72074411404630734, 0.74473710800900605, 0.76952880951308478,
0.79514580689252357, 0.82161557358563286, 0.84896649759946774, 0.87722791195508854,
0.90643012614631979, 0.93660445864574493, 0.96778327049280244, 1
};
const double bend_fine[256] = {
1, 1.0002256593050698, 1.0004513695322617, 1.0006771306930664,
1.0009029427989777, 1.0011288058614922, 1.0013547198921082, 1.0015806849023274,
1.0018067009036538, 1.002032767907594, 1.0022588859256572, 1.0024850549693551,
1.0027112750502025, 1.0029375461797159, 1.0031638683694153, 1.0033902416308227,
1.0036166659754628, 1.0038431414148634, 1.0040696679605541, 1.0042962456240678,
1.0045228744169397, 1.0047495543507072, 1.0049762854369111, 1.0052030676870944,
1.0054299011128027, 1.0056567857255843, 1.00588372153699, 1.006110708558573,
1.0063377468018897, 1.0065648362784985, 1.0067919769999607, 1.0070191689778405,
1.0072464122237039, 1.0074737067491204, 1.0077010525656616, 1.0079284496849015,
1.0081558981184175, 1.008383397877789, 1.008610948974598, 1.0088385514204294,
1.0090662052268706, 1.0092939104055114, 1.0095216669679448, 1.0097494749257656,
1.009977334290572, 1.0102052450739643, 1.0104332072875455, 1.0106612209429215,
1.0108892860517005, 1.0111174026254934, 1.0113455706759138, 1.0115737902145781,
1.0118020612531047, 1.0120303838031153, 1.0122587578762337, 1.012487183484087,
1.0127156606383041, 1.0129441893505169, 1.0131727696323602, 1.0134014014954713,
1.0136300849514894, 1.0138588200120575, 1.0140876066888203, 1.0143164449934257,
1.0145453349375237, 1.0147742765327674, 1.0150032697908125, 1.0152323147233171,
1.015461411341942, 1.0156905596583505, 1.0159197596842091, 1.0161490114311862,
1.0163783149109531, 1.0166076701351838, 1.0168370771155553, 1.0170665358637463,
1.0172960463914391, 1.0175256087103179, 1.0177552228320703, 1.0179848887683858,
1.0182146065309567, 1.0184443761314785, 1.0186741975816487, 1.0189040708931674,
1.0191339960777379, 1.0193639731470658, 1.0195940021128593, 1.0198240829868295,
1.0200542157806898, 1.0202844005061564, 1.0205146371749483, 1.0207449257987866,
1.0209752663893958, 1.0212056589585028, 1.0214361035178368, 1.0216666000791297,
1.0218971486541166, 1.0221277492545349, 1.0223584018921241, 1.0225891065786274,
1.0228198633257899, 1.0230506721453596, 1.023281533049087, 1.0235124460487257,
1.0237434111560313, 1.0239744283827625, 1.0242054977406807, 1.0244366192415495,
1.0246677928971357, 1.0248990187192082, 1.025130296719539, 1.0253616269099028,
1.0255930093020766, 1.0258244439078401, 1.0260559307389761, 1.0262874698072693,
1.0265190611245079, 1.0267507047024822, 1.0269824005529853, 1.027214148687813,
1.0274459491187637, 1.0276778018576387, 1.0279097069162415, 1.0281416643063788,
1.0283736740398595, 1.0286057361284953, 1.0288378505841009, 1.0290700174184932,
1.0293022366434921, 1.0295345082709197, 1.0297668323126017, 1.0299992087803651,
1.030231637686041, 1.0304641190414621, 1.0306966528584645, 1.0309292391488862,
1.0311618779245688, 1.0313945691973556, 1.0316273129790936, 1.0318601092816313,
1.0320929581168212, 1.0323258594965172, 1.0325588134325767, 1.0327918199368598,
1.0330248790212284, 1.0332579906975481, 1.0334911549776868, 1.033724371873515,
1.0339576413969056, 1.0341909635597348, 1.0344243383738811, 1.0346577658512259,
1.034891246003653, 1.0351247788430489, 1.0353583643813031, 1.0355920026303078,
1.0358256936019572, 1.0360594373081489, 1.0362932337607829, 1.0365270829717617,
1.0367609849529913, 1.0369949397163791, 1.0372289472738365, 1.0374630076372766,
1.0376971208186156, 1.0379312868297725, 1.0381655056826686, 1.0383997773892284,
1.0386341019613787, 1.0388684794110492, 1.0391029097501721, 1.0393373929906822,
1.0395719291445176, 1.0398065182236185, 1.0400411602399278, 1.0402758552053915,
1.0405106031319582, 1.0407454040315787, 1.0409802579162071, 1.0412151647977996,
1.0414501246883161, 1.0416851375997183, 1.0419202035439705, 1.0421553225330404,
1.042390494578898, 1.042625719693516, 1.0428609978888699, 1.043096329176938,
1.0433317135697009, 1.0435671510791424, 1.0438026417172486, 1.0440381854960086,
1.0442737824274138, 1.044509432523459, 1.044745135796141, 1.0449808922574599,
1.0452167019194181, 1.0454525647940205, 1.0456884808932754, 1.0459244502291931,
1.0461604728137874, 1.0463965486590741, 1.046632677777072, 1.0468688601798024,
1.0471050958792898, 1.047341384887561, 1.0475777272166455, 1.047814122878576,
1.048050571885387, 1.0482870742491166, 1.0485236299818055, 1.0487602390954964,
1.0489969016022356, 1.0492336175140715, 1.0494703868430555, 1.0497072096012419,
1.0499440858006872, 1.0501810154534512, 1.050417998571596, 1.0506550351671864,
1.0508921252522903, 1.0511292688389782, 1.0513664659393229, 1.0516037165654004,
1.0518410207292894, 1.0520783784430709, 1.0523157897188296, 1.0525532545686513,
1.0527907730046264, 1.0530283450388465, 1.0532659706834067, 1.0535036499504049,
1.0537413828519411, 1.0539791694001188, 1.0542170096070436, 1.0544549034848243,
1.0546928510455722, 1.0549308523014012, 1.0551689072644284, 1.0554070159467728,
1.0556451783605572, 1.0558833945179062, 1.0561216644309479, 1.0563599881118126,
1.0565983655726334, 1.0568367968255465, 1.0570752818826903, 1.0573138207562065,
1.057552413458239, 1.0577910600009348, 1.0580297603964437, 1.058268514656918,
1.0585073227945128, 1.0587461848213857, 1.058985100749698, 1.0592240705916123
};
const double bend_coarse[128] = {
1, 1.0594630943592953, 1.122462048309373, 1.189207115002721,
1.2599210498948732, 1.3348398541700344, 1.4142135623730951, 1.4983070768766815,
1.5874010519681994, 1.681792830507429, 1.7817974362806785, 1.8877486253633868,
2, 2.1189261887185906, 2.244924096618746, 2.3784142300054421,
2.5198420997897464, 2.6696797083400687, 2.8284271247461903, 2.996614153753363,
3.1748021039363992, 3.363585661014858, 3.5635948725613571, 3.7754972507267741,
4, 4.2378523774371812, 4.4898481932374912, 4.7568284600108841,
5.0396841995794928, 5.3393594166801366, 5.6568542494923806, 5.993228307506727,
6.3496042078727974, 6.727171322029716, 7.1271897451227151, 7.5509945014535473,
8, 8.4757047548743625, 8.9796963864749824, 9.5136569200217682,
10.079368399158986, 10.678718833360273, 11.313708498984761, 11.986456615013454,
12.699208415745595, 13.454342644059432, 14.25437949024543, 15.101989002907095,
16, 16.951409509748721, 17.959392772949972, 19.027313840043536,
20.158736798317967, 21.357437666720553, 22.627416997969522, 23.972913230026901,
25.398416831491197, 26.908685288118864, 28.508758980490853, 30.203978005814196,
32, 33.902819019497443, 35.918785545899944, 38.054627680087073,
40.317473596635935, 42.714875333441107, 45.254833995939045, 47.945826460053802,
50.796833662982394, 53.817370576237728, 57.017517960981706, 60.407956011628393,
64, 67.805638038994886, 71.837571091799887, 76.109255360174146,
80.63494719327187, 85.429750666882214, 90.509667991878089, 95.891652920107603,
101.59366732596479, 107.63474115247546, 114.03503592196341, 120.81591202325679,
128, 135.61127607798977, 143.67514218359977, 152.21851072034829,
161.26989438654374, 170.85950133376443, 181.01933598375618, 191.78330584021521,
203.18733465192958, 215.26948230495091, 228.07007184392683, 241.63182404651357,
256, 271.22255215597971, 287.35028436719938, 304.43702144069658,
322.53978877308765, 341.71900266752868, 362.03867196751236, 383.56661168043064,
406.37466930385892, 430.53896460990183, 456.14014368785394, 483.26364809302686,
512, 542.44510431195943, 574.70056873439876, 608.87404288139317,
645.0795775461753, 683.43800533505737, 724.07734393502471, 767.13322336086128,
812.74933860771785, 861.07792921980365, 912.28028737570787, 966.52729618605372,
1024, 1084.8902086239189, 1149.4011374687975, 1217.7480857627863,
1290.1591550923506, 1366.8760106701147, 1448.1546878700494, 1534.2664467217226
};

View File

@@ -0,0 +1,30 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
tables.h
*/
#include <math.h>
#define sine(x) (sin((2*PI/1024.0) * (x)))
#define SINE_CYCLE_LENGTH 1024
extern const sint32 freq_table[];
extern const double vol_table[];
extern const double bend_fine[];
extern const double bend_coarse[];

View File

@@ -0,0 +1,606 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "readmidi.h"
#include "output.h"
#include "tables.h"
MidToneBank *master_tonebank[128], *master_drumset[128];
static char def_instr_name[256] = "";
#define MAXWORDS 10
/* Quick-and-dirty fgets() replacement. */
static char *__fgets(char *s, int size, FILE *fp)
{
int num_read = 0;
int newline = 0;
while (num_read < size && !newline)
{
if (fread(&s[num_read], 1, 1, fp) != 1)
break;
/* Unlike fgets(), don't store newline. Under Windows/DOS we'll
* probably get an extra blank line for every line that's being
* read, but that should be ok.
*/
if (s[num_read] == '\n' || s[num_read] == '\r')
{
s[num_read] = '\0';
newline = 1;
}
num_read++;
}
s[num_read] = '\0';
return (num_read != 0) ? s : NULL;
}
static int read_config_file(char *name)
{
FILE *fp;
char tmp[1024], *w[MAXWORDS], *cp;
MidToneBank *bank=0;
int i, j, k, line=0, words;
static int rcf_count=0;
if (rcf_count>50)
{
DEBUG_MSG("Probable source loop in configuration files\n");
return (-1);
}
if (!(fp=open_file(name)))
return -1;
while (__fgets(tmp, sizeof(tmp), fp))
{
line++;
w[words=0]=strtok(tmp, " \t\240");
if (!w[0]) continue;
/* Originally the TiMidity++ extensions were prefixed like this */
if (strcmp(w[0], "#extension") == 0)
words = -1;
else if (*w[0] == '#')
continue;
while (w[words] && *w[words] != '#' && (words < MAXWORDS))
w[++words]=strtok(0," \t\240");
/*
* TiMidity++ adds a number of extensions to the config file format.
* Many of them are completely irrelevant to SDL_sound, but at least
* we shouldn't choke on them.
*
* Unfortunately the documentation for these extensions is often quite
* vague, gramatically strange or completely absent.
*/
if (
!strcmp(w[0], "comm") /* "comm" program second */
|| !strcmp(w[0], "HTTPproxy") /* "HTTPproxy" hostname:port */
|| !strcmp(w[0], "FTPproxy") /* "FTPproxy" hostname:port */
|| !strcmp(w[0], "mailaddr") /* "mailaddr" your-mail-address */
|| !strcmp(w[0], "opt") /* "opt" timidity-options */
)
{
/*
* + "comm" sets some kind of comment -- the documentation is too
* vague for me to understand at this time.
* + "HTTPproxy", "FTPproxy" and "mailaddr" are for reading data
* over a network, rather than from the file system.
* + "opt" specifies default options for TiMidity++.
*
* These are all quite useless for our version of TiMidity, so
* they can safely remain no-ops.
*/
} else if (!strcmp(w[0], "timeout")) /* "timeout" program second */
{
/*
* Specifies a timeout value of the program. A number of seconds
* before TiMidity kills the note. This may be useful to implement
* later, but I don't see any urgent need for it.
*/
DEBUG_MSG("FIXME: Implement \"timeout\" in TiMidity config.\n");
} else if (!strcmp(w[0], "copydrumset") /* "copydrumset" drumset */
|| !strcmp(w[0], "copybank")) /* "copybank" bank */
{
/*
* Copies all the settings of the specified drumset or bank to
* the current drumset or bank. May be useful later, but not a
* high priority.
*/
DEBUG_MSG("FIXME: Implement \"%s\" in TiMidity config.\n", w[0]);
} else if (!strcmp(w[0], "undef")) /* "undef" progno */
{
/*
* Undefines the tone "progno" of the current tone bank (or
* drum set?). Not a high priority.
*/
DEBUG_MSG("FIXME: Implement \"undef\" in TiMidity config.\n");
} else if (!strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */
{
/*
* Sets the alternate assign for drum set. Whatever that's
* supposed to mean.
*/
DEBUG_MSG("FIXME: Implement \"altassign\" in TiMidity config.\n");
} else if (!strcmp(w[0], "soundfont")
|| !strcmp(w[0], "font"))
{
/*
* I can't find any documentation for these, but I guess they're
* an alternative way of loading/unloading instruments.
*
* "soundfont" sf_file "remove"
* "soundfont" sf_file ["order=" order] ["cutoff=" cutoff]
* ["reso=" reso] ["amp=" amp]
* "font" "exclude" bank preset keynote
* "font" "order" order bank preset keynote
*/
DEBUG_MSG("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0]);
} else if (!strcmp(w[0], "progbase"))
{
/*
* The documentation for this makes absolutely no sense to me, but
* apparently it sets some sort of base offset for tone numbers.
* Why anyone would want to do this is beyond me.
*/
DEBUG_MSG("FIXME: Implement \"progbase\" in TiMidity config.\n");
} else if (!strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */
{
/*
* This extension is the one we will need to implement, as it is
* used by the "eawpats". Unfortunately I cannot find any
* documentation whatsoever for it, but it looks like it's used
* for remapping one instrument to another somehow.
*/
DEBUG_MSG("FIXME: Implement \"map\" in TiMidity config.\n");
}
/* Standard TiMidity config */
else if (!strcmp(w[0], "dir"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No directory given\n", name, line);
return -2;
}
for (i=1; i<words; i++)
add_to_pathlist(w[i]);
}
else if (!strcmp(w[0], "source"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No file name given\n", name, line);
return -2;
}
for (i=1; i<words; i++)
{
rcf_count++;
read_config_file(w[i]);
rcf_count--;
}
}
else if (!strcmp(w[0], "default"))
{
if (words != 2)
{
DEBUG_MSG("%s: line %d: Must specify exactly one patch name\n",
name, line);
return -2;
}
strncpy(def_instr_name, w[1], 255);
def_instr_name[255]='\0';
}
else if (!strcmp(w[0], "drumset"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No drum set number given\n", name, line);
return -2;
}
i=atoi(w[1]);
if (i<0 || i>127)
{
DEBUG_MSG("%s: line %d: Drum set must be between 0 and 127\n",
name, line);
return -2;
}
if (!master_drumset[i])
{
master_drumset[i] = safe_malloc(sizeof(MidToneBank));
memset(master_drumset[i], 0, sizeof(MidToneBank));
master_drumset[i]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_drumset[i]->tone, 0, 128 * sizeof(MidToneBankElement));
}
bank=master_drumset[i];
}
else if (!strcmp(w[0], "bank"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No bank number given\n", name, line);
return -2;
}
i=atoi(w[1]);
if (i<0 || i>127)
{
DEBUG_MSG("%s: line %d: Tone bank must be between 0 and 127\n",
name, line);
return -2;
}
if (!master_tonebank[i])
{
master_tonebank[i] = safe_malloc(sizeof(MidToneBank));
memset(master_tonebank[i], 0, sizeof(MidToneBank));
master_tonebank[i]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_tonebank[i]->tone, 0, 128 * sizeof(MidToneBankElement));
}
bank=master_tonebank[i];
}
else
{
if ((words < 2) || (*w[0] < '0' || *w[0] > '9'))
{
DEBUG_MSG("%s: line %d: syntax error\n", name, line);
return -2;
}
i=atoi(w[0]);
if (i<0 || i>127)
{
DEBUG_MSG("%s: line %d: Program must be between 0 and 127\n",
name, line);
return -2;
}
if (!bank)
{
DEBUG_MSG("%s: line %d: Must specify tone bank or drum set before assignment\n",
name, line);
return -2;
}
if (bank->tone[i].name)
free(bank->tone[i].name);
strcpy((bank->tone[i].name=safe_malloc(strlen(w[1])+1)),w[1]);
bank->tone[i].note=bank->tone[i].amp=bank->tone[i].pan=
bank->tone[i].strip_loop=bank->tone[i].strip_envelope=
bank->tone[i].strip_tail=-1;
for (j=2; j<words; j++)
{
if (!(cp=strchr(w[j], '=')))
{
DEBUG_MSG("%s: line %d: bad patch option %s\n", name, line, w[j]);
return -2;
}
*cp++=0;
if (!strcmp(w[j], "amp"))
{
k=atoi(cp);
if ((k<0 || k>MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9'))
{
DEBUG_MSG("%s: line %d: amplification must be between 0 and %d\n",
name, line, MAX_AMPLIFICATION);
return -2;
}
bank->tone[i].amp=k;
}
else if (!strcmp(w[j], "note"))
{
k=atoi(cp);
if ((k<0 || k>127) || (*cp < '0' || *cp > '9'))
{
DEBUG_MSG("%s: line %d: note must be between 0 and 127\n",
name, line);
return -2;
}
bank->tone[i].note=k;
}
else if (!strcmp(w[j], "pan"))
{
if (!strcmp(cp, "center"))
k=64;
else if (!strcmp(cp, "left"))
k=0;
else if (!strcmp(cp, "right"))
k=127;
else
k=((atoi(cp)+100) * 100) / 157;
if ((k<0 || k>127) || (k==0 && *cp!='-' && (*cp < '0' || *cp > '9')))
{
DEBUG_MSG("%s: line %d: panning must be left, right, center, or between -100 and 100\n",
name, line);
return -2;
}
bank->tone[i].pan=k;
}
else if (!strcmp(w[j], "keep"))
{
if (!strcmp(cp, "env"))
bank->tone[i].strip_envelope=0;
else if (!strcmp(cp, "loop"))
bank->tone[i].strip_loop=0;
else
{
DEBUG_MSG("%s: line %d: keep must be env or loop\n", name, line);
return -2;
}
}
else if (!strcmp(w[j], "strip"))
{
if (!strcmp(cp, "env"))
bank->tone[i].strip_envelope=1;
else if (!strcmp(cp, "loop"))
bank->tone[i].strip_loop=1;
else if (!strcmp(cp, "tail"))
bank->tone[i].strip_tail=1;
else
{
DEBUG_MSG("%s: line %d: strip must be env, loop, or tail\n",
name, line);
return -2;
}
}
else
{
DEBUG_MSG("%s: line %d: bad patch option %s\n", name, line, w[j]);
return -2;
}
}
}
}
fclose(fp);
return 0;
}
int mid_init_no_config()
{
/* Allocate memory for the standard tonebank and drumset */
master_tonebank[0] = safe_malloc(sizeof(MidToneBank));
memset(master_tonebank[0], 0, sizeof(MidToneBank));
master_tonebank[0]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_tonebank[0]->tone, 0, 128 * sizeof(MidToneBankElement));
master_drumset[0] = safe_malloc(sizeof(MidToneBank));
memset(master_drumset[0], 0, sizeof(MidToneBank));
master_drumset[0]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_drumset[0]->tone, 0, 128 * sizeof(MidToneBankElement));
return 0;
}
int mid_init(char *config_file)
{
/* !!! FIXME: This may be ugly, but slightly less so than requiring the
* default search path to have only one element. I think.
*
* We only need to include the likely locations for the config
* file itself since that file should contain any other directory
* that needs to be added to the search path.
*/
#ifdef WIN32
add_to_pathlist("\\TIMIDITY");
#else
add_to_pathlist("/usr/local/lib/timidity");
add_to_pathlist("/etc");
#endif
mid_init_no_config();
if (config_file == NULL || *config_file == '\0')
config_file = CONFIG_FILE;
return read_config_file(config_file);
}
MidSong *mid_song_load_dls(MidIStream *stream, MidDLSPatches *patches, MidSongOptions *options)
{
MidSong *song;
int i;
if (stream == NULL)
return NULL;
/* Allocate memory for the song */
song = (MidSong *)safe_malloc(sizeof(*song));
memset(song, 0, sizeof(*song));
song->patches = patches;
for (i = 0; i < 128; i++)
{
if (master_tonebank[i])
{
song->tonebank[i] = safe_malloc(sizeof(MidToneBank));
memset(song->tonebank[i], 0, sizeof(MidToneBank));
song->tonebank[i]->tone = master_tonebank[i]->tone;
}
if (master_drumset[i])
{
song->drumset[i] = safe_malloc(sizeof(MidToneBank));
memset(song->drumset[i], 0, sizeof(MidToneBank));
song->drumset[i]->tone = master_drumset[i]->tone;
}
}
song->amplification = DEFAULT_AMPLIFICATION;
song->voices = DEFAULT_VOICES;
song->drumchannels = DEFAULT_DRUMCHANNELS;
song->rate = options->rate;
song->encoding = 0;
if ((options->format & 0xFF) == 16)
song->encoding |= PE_16BIT;
if (options->format & 0x8000)
song->encoding |= PE_SIGNED;
if (options->channels == 1)
song->encoding |= PE_MONO;
switch (options->format) {
case MID_AUDIO_S8:
song->write = s32tos8;
break;
case MID_AUDIO_U8:
song->write = s32tou8;
break;
case MID_AUDIO_S16LSB:
song->write = s32tos16l;
break;
case MID_AUDIO_S16MSB:
song->write = s32tos16b;
break;
case MID_AUDIO_U16LSB:
song->write = s32tou16l;
break;
default:
DEBUG_MSG("Unsupported audio format\n");
song->write = s32tou16l;
break;
}
song->buffer_size = options->buffer_size;
song->resample_buffer = safe_malloc(options->buffer_size * sizeof(sample_t));
song->common_buffer = safe_malloc(options->buffer_size * 2 * sizeof(sint32));
song->bytes_per_sample =
((song->encoding & PE_MONO) ? 1 : 2)
* ((song->encoding & PE_16BIT) ? 2 : 1);
song->control_ratio = options->rate / CONTROLS_PER_SECOND;
if (song->control_ratio < 1)
song->control_ratio = 1;
else if (song->control_ratio > MAX_CONTROL_RATIO)
song->control_ratio = MAX_CONTROL_RATIO;
song->lost_notes = 0;
song->cut_notes = 0;
song->events = read_midi_file(stream, song, &(song->groomed_event_count),
&song->samples);
/* Make sure everything is okay */
if (!song->events) {
free(song);
return(NULL);
}
song->default_instrument = 0;
song->default_program = DEFAULT_PROGRAM;
if (*def_instr_name)
set_default_instrument(song, def_instr_name);
load_missing_instruments(song);
return(song);
}
MidSong *mid_song_load(MidIStream *stream, MidSongOptions *options)
{
return mid_song_load_dls(stream, NULL, options);
}
void mid_song_free(MidSong *song)
{
int i;
free_instruments(song);
for (i = 0; i < 128; i++)
{
if (song->tonebank[i])
free(song->tonebank[i]);
if (song->drumset[i])
free(song->drumset[i]);
}
free(song->common_buffer);
free(song->resample_buffer);
free(song->events);
for (i = 0; i < (sizeof(song->meta_data) / sizeof(song->meta_data[0])); i++)
{
if (song->meta_data[i])
free(song->meta_data[i]);
}
free(song);
}
void mid_exit(void)
{
int i, j;
for (i = 0; i < 128; i++)
{
if (master_tonebank[i])
{
MidToneBankElement *e = master_tonebank[i]->tone;
if (e != NULL)
{
for (j = 0; j < 128; j++)
{
if (e[j].name != NULL)
free(e[j].name);
}
free(e);
}
free(master_tonebank[i]);
}
if (master_drumset[i])
{
MidToneBankElement *e = master_drumset[i]->tone;
if (e != NULL)
{
for (j = 0; j < 128; j++)
{
if (e[j].name != NULL)
free(e[j].name);
}
free(e);
}
free(master_drumset[i]);
}
}
free_pathlist();
}

View File

@@ -0,0 +1,206 @@
/*
libTiMidity -- MIDI to WAVE converter library
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
Copyright (C) 2004 Konstantin Korikov <lostclus@ua.fm>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef TIMIDITY_H
#define TIMIDITY_H
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define LIBTIMIDITY_VERSION_MAJOR @LIBTIMIDITY_MAJOR_VERSION@L
#define LIBTIMIDITY_VERSION_MINOR @LIBTIMIDITY_MINOR_VERSION@L
#define LIBTIMIDITY_PATCHLEVEL @LIBTIMIDITY_MICRO_VERSION@L
#define LIBTIMIDITY_VERSION \
((LIBTIMIDITY_VERSION_MAJOR<<16)| \
(LIBTIMIDITY_VERSION_MINOR<< 8)| \
(LIBTIMIDITY_PATCHLEVEL))
/* Audio format flags (defaults to LSB byte order)
*/
#define MID_AUDIO_U8 0x0008 /* Unsigned 8-bit samples */
#define MID_AUDIO_S8 0x8008 /* Signed 8-bit samples */
#define MID_AUDIO_U16LSB 0x0010 /* Unsigned 16-bit samples */
#define MID_AUDIO_S16LSB 0x8010 /* Signed 16-bit samples */
#define MID_AUDIO_U16MSB 0x1010 /* As above, but big-endian byte order */
#define MID_AUDIO_S16MSB 0x9010 /* As above, but big-endian byte order */
#define MID_AUDIO_U16 MID_AUDIO_U16LSB
#define MID_AUDIO_S16 MID_AUDIO_S16LSB
/* Core Library Types
*/
typedef unsigned char uint8;
typedef signed char sint8;
typedef unsigned short uint16;
typedef signed short sint16;
typedef unsigned int uint32;
typedef signed int sint32;
typedef size_t (*MidIStreamReadFunc) (void *ctx, void *ptr, size_t size,
size_t nmemb);
typedef int (*MidIStreamCloseFunc) (void *ctx);
typedef struct _MidIStream MidIStream;
typedef struct _MidDLSPatches MidDLSPatches;
typedef struct _MidSong MidSong;
typedef struct _MidSongOptions MidSongOptions;
struct _MidSongOptions
{
sint32 rate; /* DSP frequency -- samples per second */
uint16 format; /* Audio data format */
uint8 channels; /* Number of channels: 1 mono, 2 stereo */
uint16 buffer_size; /* Sample buffer size in samples */
};
typedef enum
{
MID_SONG_TEXT = 0,
MID_SONG_COPYRIGHT = 1
} MidSongMetaId;
/* Core Library Functions
* ======================
*/
/* Initialize the library. If config_file is NULL
* search for configuratin file in default directories
*/
extern int mid_init (char *config_file);
/* Initialize the library without reading any
* configuratin file
*/
extern int mid_init_no_config (void);
/* Shutdown the library
*/
extern void mid_exit (void);
/* Input Stream Functions
* ======================
*/
/* Create input stream from a file name
*/
extern MidIStream *mid_istream_open_file (const char *file);
/* Create input stream from a file pointer
*/
extern MidIStream *mid_istream_open_fp (FILE * fp, int autoclose);
/* Create input stream from memory
*/
extern MidIStream *mid_istream_open_mem (void *mem, size_t size,
int autofree);
/* Create custom input stream
*/
extern MidIStream *mid_istream_open_callbacks (MidIStreamReadFunc read,
MidIStreamCloseFunc close,
void *context);
/* Read data from input stream
*/
extern size_t mid_istream_read (MidIStream * stream, void *ptr, size_t size,
size_t nmemb);
/* Skip data from input stream
*/
extern void mid_istream_skip (MidIStream * stream, size_t len);
/* Close and destroy input stream
*/
extern int mid_istream_close (MidIStream * stream);
/* DLS Pathes Functions
* ====================
*/
/* Load DLS patches
*/
extern MidDLSPatches *mid_dlspatches_load (MidIStream * stream);
/* Destroy DLS patches
*/
extern void mid_dlspatches_free (MidDLSPatches * patches);
/* MIDI Song Functions
* ===================
*/
/* Load MIDI song
*/
extern MidSong *mid_song_load (MidIStream * stream,
MidSongOptions * options);
/* Load MIDI song with specified DLS pathes
*/
extern MidSong *mid_song_load_dls (MidIStream * stream,
MidDLSPatches * patches,
MidSongOptions * options);
/* Set song amplification value
*/
extern void mid_song_set_volume (MidSong * song, int volume);
/* Seek song to the start position and initialize conversion
*/
extern void mid_song_start (MidSong * song);
/* Read WAVE data
*/
extern size_t mid_song_read_wave (MidSong * song, void *ptr, size_t size);
/* Seek song to specified offset in millseconds
*/
extern void mid_song_seek (MidSong * song, uint32 ms);
/* Get total song time in millseconds
*/
extern uint32 mid_song_get_total_time (MidSong * song);
/* Get current song time in millseconds
*/
extern uint32 mid_song_get_time (MidSong * song);
/* Get song meta data. Return NULL if no meta data found
*/
extern char *mid_song_get_meta (MidSong * song, MidSongMetaId what);
/* Destroy song
*/
extern void mid_song_free (MidSong * song);
#ifdef __cplusplus
}
#endif
#endif /* TIMIDITY_H */

View File

@@ -0,0 +1,192 @@
#ifndef TIMIDITY_INTERNAL_H
#define TIMIDITY_INTERNAL_H
#include "timidity.h"
#if defined(__i386__) || defined(__ia64__) || defined(WIN32) || \
(defined(__alpha__) || defined(__alpha)) || \
defined(__arm__) || \
(defined(__mips__) && defined(__MIPSEL__)) || \
defined(__SYMBIAN32__) || \
defined(__x86_64__) || \
defined(__LITTLE_ENDIAN__)
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN
#endif
#undef BIG_ENDIAN
#else
#ifndef BIG_ENDIAN
#define BIG_ENDIAN
#endif
#undef LITTLE_ENDIAN
#endif
/* Instrument files are little-endian, MIDI files big-endian, so we
need to do some conversions. */
#define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
#ifdef __i486__
# define XCHG_LONG(x) \
({ sint32 __value; \
asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \
__value; })
#else
# define XCHG_LONG(x) ((((x)&0xFF)<<24) | \
(((x)&0xFF00)<<8) | \
(((x)&0xFF0000)>>8) | \
(((x)>>24)&0xFF))
#endif
#ifdef LITTLE_ENDIAN
#define SWAPLE16(x) x
#define SWAPLE32(x) x
#define SWAPBE16(x) XCHG_SHORT(x)
#define SWAPBE32(x) XCHG_LONG(x)
#else
#define SWAPBE16(x) x
#define SWAPBE32(x) x
#define SWAPLE16(x) XCHG_SHORT(x)
#define SWAPLE32(x) XCHG_LONG(x)
#endif
#ifdef DEBUG
#define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__)
#else
#define DEBUG_MSG(...)
#endif
#define MID_VIBRATO_SAMPLE_INCREMENTS 32
/* Maximum polyphony. */
#define MID_MAX_VOICES 48
typedef sint16 sample_t;
typedef sint32 final_volume_t;
typedef struct _MidSample MidSample;
struct _MidSample
{
sint32
loop_start, loop_end, data_length,
sample_rate, low_vel, high_vel, low_freq, high_freq, root_freq;
sint32 envelope_rate[6], envelope_offset[6];
float volume;
sample_t *data;
sint32
tremolo_sweep_increment, tremolo_phase_increment,
vibrato_sweep_increment, vibrato_control_ratio;
uint8 tremolo_depth, vibrato_depth, modes;
sint8 panning, note_to_use;
};
typedef struct _MidChannel MidChannel;
struct _MidChannel
{
int bank, program, volume, sustain, panning, pitchbend, expression;
int mono; /* one note only on this channel -- not implemented yet */
int pitchsens;
/* chorus, reverb... Coming soon to a 300-MHz, eight-way superscalar
processor near you */
float pitchfactor; /* precomputed pitch bend factor to save some fdiv's */
};
typedef struct _MidVoice MidVoice;
struct _MidVoice
{
uint8 status, channel, note, velocity;
MidSample *sample;
sint32
orig_frequency, frequency,
sample_offset, sample_increment,
envelope_volume, envelope_target, envelope_increment,
tremolo_sweep, tremolo_sweep_position,
tremolo_phase, tremolo_phase_increment,
vibrato_sweep, vibrato_sweep_position;
final_volume_t left_mix, right_mix;
float left_amp, right_amp, tremolo_volume;
sint32 vibrato_sample_increment[MID_VIBRATO_SAMPLE_INCREMENTS];
int
vibrato_phase, vibrato_control_ratio, vibrato_control_counter,
envelope_stage, control_counter, panning, panned;
};
typedef struct _MidInstrument MidInstrument;
struct _MidInstrument
{
int samples;
MidSample *sample;
};
typedef struct _MidToneBankElement MidToneBankElement;
struct _MidToneBankElement
{
char *name;
int note, amp, pan, strip_loop, strip_envelope, strip_tail;
};
typedef struct _MidToneBank MidToneBank;
struct _MidToneBank
{
MidToneBankElement *tone;
MidInstrument *instrument[128];
};
typedef struct _MidEvent MidEvent;
struct _MidEvent
{
sint32 time;
uint8 channel, type, a, b;
};
typedef struct _MidEventList MidEventList;
struct _MidEventList
{
MidEvent event;
void *next;
};
struct _MidSong
{
int playing;
sint32 rate;
sint32 encoding;
int bytes_per_sample;
float master_volume;
sint32 amplification;
MidDLSPatches *patches;
MidToneBank *tonebank[128];
MidToneBank *drumset[128];
MidInstrument *default_instrument;
int default_program;
void (*write) (void *dp, sint32 * lp, sint32 c);
int buffer_size;
sample_t *resample_buffer;
sint32 *common_buffer;
/* These would both fit into 32 bits, but they are often added in
large multiples, so it's simpler to have two roomy ints */
/* samples per MIDI delta-t */
sint32 sample_increment;
sint32 sample_correction;
MidChannel channel[16];
MidVoice voice[MID_MAX_VOICES];
int voices;
sint32 drumchannels;
sint32 control_ratio;
sint32 lost_notes;
sint32 cut_notes;
sint32 samples;
MidEvent *events;
MidEvent *current_event;
MidEventList *evlist;
sint32 current_sample;
sint32 event_count;
sint32 at;
sint32 groomed_event_count;
char *meta_data[8];
};
#endif /* TIMIDITY_INTERNAL_H */