timidity/timidity.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 21:54:04 -0700
changeset 782 e7d3a8f73e88
parent 701 25605697981c
child 799 82dcc7ce6d20
permissions -rw-r--r--
Merged over timidity from SDL_sound
This has changes to make it safe to load MIDI files as sound chunks and adds the ability to seek in MIDI files
Also cherry picked some patches from the original SDL_mixer timidity to fix buffer overruns and so forth.
slouken@0
     1
/*
slouken@782
     2
slouken@0
     3
    TiMidity -- Experimental MIDI to WAVE converter
slouken@0
     4
    Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
slouken@0
     5
slouken@518
     6
    This program is free software; you can redistribute it and/or modify
slouken@518
     7
    it under the terms of the Perl Artistic License, available in COPYING.
slouken@782
     8
*/
slouken@782
     9
slouken@782
    10
#if HAVE_CONFIG_H
slouken@782
    11
#  include <config.h>
slouken@782
    12
#endif
slouken@0
    13
slouken@0
    14
#include <stdio.h>
slouken@0
    15
#include <stdlib.h>
slouken@0
    16
#include <string.h>
slouken@0
    17
slouken@24
    18
#include "SDL.h"
slouken@782
    19
slouken@782
    20
#include "timidity.h"
slouken@782
    21
slouken@782
    22
#include "options.h"
slouken@0
    23
#include "common.h"
slouken@0
    24
#include "instrum.h"
slouken@0
    25
#include "playmidi.h"
slouken@0
    26
#include "readmidi.h"
slouken@0
    27
#include "output.h"
slouken@0
    28
slouken@0
    29
#include "tables.h"
slouken@0
    30
slouken@782
    31
ToneBank *master_tonebank[MAXBANK], *master_drumset[MAXBANK];
slouken@0
    32
slouken@782
    33
static char def_instr_name[256] = "";
slouken@0
    34
slouken@0
    35
#define MAXWORDS 10
slouken@0
    36
slouken@782
    37
/* Quick-and-dirty fgets() replacement. */
slouken@782
    38
slouken@782
    39
static char *RWgets(SDL_RWops *rw, char *s, int size)
slouken@782
    40
{
slouken@782
    41
    int num_read = 0;
slouken@782
    42
    char *p = s;
slouken@782
    43
slouken@782
    44
    --size;/* so that we nul terminate properly */
slouken@782
    45
slouken@782
    46
    for (; num_read < size; ++p)
slouken@782
    47
    {
slouken@782
    48
	if (SDL_RWread(rw, p, 1, 1) != 1)
slouken@782
    49
	    break;
slouken@782
    50
slouken@782
    51
	num_read++;
slouken@782
    52
slouken@782
    53
	/* Unlike fgets(), don't store newline. Under Windows/DOS we'll
slouken@782
    54
	 * probably get an extra blank line for every line that's being
slouken@782
    55
	 * read, but that should be ok.
slouken@782
    56
	 */
slouken@782
    57
	if (*p == '\n' || *p == '\r')
slouken@782
    58
	{
slouken@782
    59
	    *p = '\0';
slouken@782
    60
	    return s;
slouken@782
    61
	}
slouken@782
    62
    }
slouken@782
    63
slouken@782
    64
    *p = '\0';
slouken@782
    65
slouken@782
    66
    return (num_read != 0) ? s : NULL;
slouken@782
    67
}
slouken@782
    68
slouken@421
    69
static int read_config_file(const char *name)
slouken@0
    70
{
slouken@782
    71
  SDL_RWops *rw;
slouken@782
    72
  char tmp[1024], *w[MAXWORDS], *cp;
slouken@0
    73
  ToneBank *bank=0;
slouken@0
    74
  int i, j, k, line=0, words;
slouken@0
    75
  static int rcf_count=0;
slouken@0
    76
slouken@0
    77
  if (rcf_count>50)
slouken@782
    78
  {
slouken@782
    79
    SNDDBG(("Probable source loop in configuration files\n"));
slouken@0
    80
    return (-1);
slouken@782
    81
  }
slouken@0
    82
slouken@782
    83
  if (!(rw=open_file(name)))
slouken@0
    84
   return -1;
slouken@0
    85
slouken@782
    86
  while (RWgets(rw, tmp, sizeof(tmp)))
slouken@245
    87
  {
slouken@245
    88
    line++;
slouken@782
    89
    words=0;
slouken@782
    90
    w[0]=strtok(tmp, " \t\240");
slouken@782
    91
    if (!w[0]) continue;
slouken@782
    92
slouken@782
    93
        /* Originally the TiMidity++ extensions were prefixed like this */
slouken@782
    94
    if (strcmp(w[0], "#extension") == 0)
slouken@782
    95
    {
slouken@782
    96
        w[0]=strtok(0, " \t\240");
slouken@782
    97
        if (!w[0]) continue;
slouken@782
    98
    }
slouken@782
    99
slouken@782
   100
    if (*w[0] == '#')
slouken@782
   101
        continue;
slouken@782
   102
slouken@782
   103
    while (w[words] && *w[words] != '#' && (words < (MAXWORDS-1)))
slouken@782
   104
      w[++words]=strtok(0," \t\240");
slouken@782
   105
slouken@782
   106
        /*
slouken@782
   107
         * TiMidity++ adds a number of extensions to the config file format.
slouken@782
   108
         * Many of them are completely irrelevant to SDL_sound, but at least
slouken@782
   109
         * we shouldn't choke on them.
slouken@782
   110
         *
slouken@782
   111
         * Unfortunately the documentation for these extensions is often quite
slouken@782
   112
         * vague, gramatically strange or completely absent.
slouken@782
   113
         */
slouken@782
   114
    if (
slouken@782
   115
           !strcmp(w[0], "comm")      /* "comm" program second        */
slouken@782
   116
        || !strcmp(w[0], "HTTPproxy") /* "HTTPproxy" hostname:port    */
slouken@782
   117
        || !strcmp(w[0], "FTPproxy")  /* "FTPproxy" hostname:port     */
slouken@782
   118
        || !strcmp(w[0], "mailaddr")  /* "mailaddr" your-mail-address */
slouken@782
   119
        || !strcmp(w[0], "opt")       /* "opt" timidity-options       */
slouken@782
   120
       )
slouken@782
   121
    {
slouken@782
   122
            /*
slouken@782
   123
             * + "comm" sets some kind of comment -- the documentation is too
slouken@782
   124
             *   vague for me to understand at this time.
slouken@782
   125
             * + "HTTPproxy", "FTPproxy" and "mailaddr" are for reading data
slouken@782
   126
             *   over a network, rather than from the file system.
slouken@782
   127
             * + "opt" specifies default options for TiMidity++.
slouken@782
   128
             *
slouken@782
   129
             * These are all quite useless for our version of TiMidity, so
slouken@782
   130
             * they can safely remain no-ops.
slouken@782
   131
             */
slouken@782
   132
    } else if (!strcmp(w[0], "timeout")) /* "timeout" program second */
slouken@782
   133
    {
slouken@782
   134
            /*
slouken@782
   135
             * Specifies a timeout value of the program. A number of seconds
slouken@782
   136
             * before TiMidity kills the note. This may be useful to implement
slouken@782
   137
             * later, but I don't see any urgent need for it.
slouken@782
   138
             */
slouken@782
   139
        SNDDBG(("FIXME: Implement \"timeout\" in TiMidity config.\n"));
slouken@782
   140
    } else if (!strcmp(w[0], "copydrumset")  /* "copydrumset" drumset */
slouken@782
   141
               || !strcmp(w[0], "copybank")) /* "copybank" bank       */
slouken@782
   142
    {
slouken@782
   143
            /*
slouken@782
   144
             * Copies all the settings of the specified drumset or bank to
slouken@782
   145
             * the current drumset or bank. May be useful later, but not a
slouken@782
   146
             * high priority.
slouken@782
   147
             */
slouken@782
   148
        SNDDBG(("FIXME: Implement \"%s\" in TiMidity config.\n", w[0]));
slouken@782
   149
    } else if (!strcmp(w[0], "undef")) /* "undef" progno */
slouken@782
   150
    {
slouken@782
   151
            /*
slouken@782
   152
             * Undefines the tone "progno" of the current tone bank (or
slouken@782
   153
             * drum set?). Not a high priority.
slouken@782
   154
             */
slouken@782
   155
        SNDDBG(("FIXME: Implement \"undef\" in TiMidity config.\n"));
slouken@782
   156
    } else if (!strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */
slouken@782
   157
    {
slouken@782
   158
            /*
slouken@782
   159
             * Sets the alternate assign for drum set. Whatever that's
slouken@782
   160
             * supposed to mean.
slouken@782
   161
             */
slouken@782
   162
        SNDDBG(("FIXME: Implement \"altassign\" in TiMidity config.\n"));
slouken@782
   163
    } else if (!strcmp(w[0], "soundfont")
slouken@782
   164
               || !strcmp(w[0], "font"))
slouken@782
   165
    {
slouken@782
   166
            /*
slouken@782
   167
             * I can't find any documentation for these, but I guess they're
slouken@782
   168
             * an alternative way of loading/unloading instruments.
slouken@782
   169
             * 
slouken@782
   170
             * "soundfont" sf_file "remove"
slouken@782
   171
             * "soundfont" sf_file ["order=" order] ["cutoff=" cutoff]
slouken@782
   172
             *                     ["reso=" reso] ["amp=" amp]
slouken@782
   173
             * "font" "exclude" bank preset keynote
slouken@782
   174
             * "font" "order" order bank preset keynote
slouken@782
   175
             */
slouken@782
   176
        SNDDBG(("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0]));
slouken@782
   177
    } else if (!strcmp(w[0], "progbase"))
slouken@782
   178
    {
slouken@782
   179
            /*
slouken@782
   180
             * The documentation for this makes absolutely no sense to me, but
slouken@782
   181
             * apparently it sets some sort of base offset for tone numbers.
slouken@782
   182
             * Why anyone would want to do this is beyond me.
slouken@782
   183
             */
slouken@782
   184
        SNDDBG(("FIXME: Implement \"progbase\" in TiMidity config.\n"));
slouken@782
   185
    } else if (!strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */
slouken@782
   186
    {
slouken@782
   187
            /*
slouken@782
   188
             * This extension is the one we will need to implement, as it is
slouken@782
   189
             * used by the "eawpats". Unfortunately I cannot find any
slouken@782
   190
             * documentation whatsoever for it, but it looks like it's used
slouken@782
   191
             * for remapping one instrument to another somehow.
slouken@782
   192
             */
slouken@782
   193
        SNDDBG(("FIXME: Implement \"map\" in TiMidity config.\n"));
slouken@782
   194
    }
slouken@782
   195
slouken@782
   196
        /* Standard TiMidity config */
slouken@782
   197
    
slouken@782
   198
    else if (!strcmp(w[0], "dir"))
slouken@245
   199
    {
slouken@245
   200
      if (words < 2)
slouken@782
   201
      {
slouken@782
   202
	SNDDBG(("%s: line %d: No directory given\n", name, line));
slouken@782
   203
	goto fail;
slouken@782
   204
      }
slouken@245
   205
      for (i=1; i<words; i++)
slouken@782
   206
	add_to_pathlist(w[i]);
slouken@245
   207
    }
slouken@782
   208
    else if (!strcmp(w[0], "source"))
slouken@782
   209
    {
slouken@782
   210
      if (words < 2)
slouken@0
   211
      {
slouken@782
   212
	SNDDBG(("%s: line %d: No file name given\n", name, line));
slouken@782
   213
	goto fail;
slouken@782
   214
      }
slouken@782
   215
      for (i=1; i<words; i++)
slouken@0
   216
      {
slouken@782
   217
	int status;
slouken@782
   218
	rcf_count++;
slouken@782
   219
	status = read_config_file(w[i]);
slouken@782
   220
	rcf_count--;
slouken@782
   221
	if (status != 0) {
slouken@782
   222
	  SDL_RWclose(rw);
slouken@782
   223
	  return status;
slouken@782
   224
	}
slouken@0
   225
      }
slouken@782
   226
    }
slouken@782
   227
    else if (!strcmp(w[0], "default"))
slouken@782
   228
    {
slouken@782
   229
      if (words != 2)
slouken@0
   230
      {
slouken@782
   231
	SNDDBG(("%s: line %d: Must specify exactly one patch name\n",
slouken@782
   232
		name, line));
slouken@782
   233
	goto fail;
slouken@0
   234
      }
slouken@782
   235
      strncpy(def_instr_name, w[1], 255);
slouken@782
   236
      def_instr_name[255]='\0';
slouken@782
   237
    }
slouken@0
   238
    else if (!strcmp(w[0], "drumset"))
slouken@782
   239
    {
slouken@782
   240
      if (words < 2)
slouken@0
   241
      {
slouken@782
   242
	SNDDBG(("%s: line %d: No drum set number given\n", name, line));
slouken@782
   243
	goto fail;
slouken@0
   244
      }
slouken@782
   245
      i=atoi(w[1]);
slouken@782
   246
      if (i<0 || i>(MAXBANK-1))
slouken@0
   247
      {
slouken@782
   248
	SNDDBG(("%s: line %d: Drum set must be between 0 and %d\n",
slouken@782
   249
		name, line, MAXBANK-1));
slouken@782
   250
	goto fail;
slouken@782
   251
      }
slouken@782
   252
      if (!master_drumset[i])
slouken@782
   253
      {
slouken@782
   254
	master_drumset[i] = safe_malloc(sizeof(ToneBank));
slouken@782
   255
	memset(master_drumset[i], 0, sizeof(ToneBank));
slouken@782
   256
	master_drumset[i]->tone = safe_malloc(128 * sizeof(ToneBankElement));
slouken@782
   257
	memset(master_drumset[i]->tone, 0, 128 * sizeof(ToneBankElement));
slouken@782
   258
      }
slouken@782
   259
      bank=master_drumset[i];
slouken@782
   260
    }
slouken@0
   261
    else if (!strcmp(w[0], "bank"))
slouken@782
   262
    {
slouken@782
   263
      if (words < 2)
slouken@0
   264
      {
slouken@782
   265
	SNDDBG(("%s: line %d: No bank number given\n", name, line));
slouken@782
   266
	goto fail;
slouken@0
   267
      }
slouken@782
   268
      i=atoi(w[1]);
slouken@782
   269
      if (i<0 || i>(MAXBANK-1))
slouken@782
   270
      {
slouken@782
   271
	SNDDBG(("%s: line %d: Tone bank must be between 0 and %d\n",
slouken@782
   272
		name, line, MAXBANK-1));
slouken@782
   273
	goto fail;
slouken@0
   274
      }
slouken@782
   275
      if (!master_tonebank[i])
slouken@782
   276
      {
slouken@782
   277
	master_tonebank[i] = safe_malloc(sizeof(ToneBank));
slouken@782
   278
	memset(master_tonebank[i], 0, sizeof(ToneBank));
slouken@782
   279
	master_tonebank[i]->tone = safe_malloc(128 * sizeof(ToneBankElement));
slouken@782
   280
	memset(master_tonebank[i]->tone, 0, 128 * sizeof(ToneBankElement));
slouken@782
   281
      }
slouken@782
   282
      bank=master_tonebank[i];
slouken@782
   283
    }
slouken@782
   284
    else
slouken@0
   285
    {
slouken@782
   286
      if ((words < 2) || (*w[0] < '0' || *w[0] > '9'))
slouken@782
   287
      {
slouken@782
   288
	SNDDBG(("%s: line %d: syntax error\n", name, line));
slouken@782
   289
	continue;
slouken@782
   290
      }
slouken@782
   291
      i=atoi(w[0]);
slouken@782
   292
      if (i<0 || i>127)
slouken@782
   293
      {
slouken@782
   294
	SNDDBG(("%s: line %d: Program must be between 0 and 127\n",
slouken@782
   295
		name, line));
slouken@782
   296
	goto fail;
slouken@782
   297
      }
slouken@782
   298
      if (!bank)
slouken@782
   299
      {
slouken@782
   300
	SNDDBG(("%s: line %d: Must specify tone bank or drum set before assignment\n",
slouken@782
   301
		name, line));
slouken@782
   302
	goto fail;
slouken@782
   303
      }
slouken@782
   304
      if (bank->tone[i].name)
slouken@782
   305
	free(bank->tone[i].name);
slouken@782
   306
      strcpy((bank->tone[i].name=safe_malloc(strlen(w[1])+1)),w[1]);
slouken@782
   307
      bank->tone[i].note=bank->tone[i].amp=bank->tone[i].pan=
slouken@782
   308
      bank->tone[i].strip_loop=bank->tone[i].strip_envelope=
slouken@0
   309
      bank->tone[i].strip_tail=-1;
slouken@0
   310
slouken@782
   311
      for (j=2; j<words; j++)
slouken@0
   312
      {
slouken@782
   313
	if (!(cp=strchr(w[j], '=')))
slouken@782
   314
	{
slouken@782
   315
	  SNDDBG(("%s: line %d: bad patch option %s\n", name, line, w[j]));
slouken@782
   316
	  goto fail;
slouken@782
   317
	}
slouken@782
   318
	*cp++=0;
slouken@782
   319
	if (!strcmp(w[j], "amp"))
slouken@782
   320
	{
slouken@782
   321
	  k=atoi(cp);
slouken@782
   322
	  if ((k<0 || k>MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9'))
slouken@782
   323
	  {
slouken@782
   324
	    SNDDBG(("%s: line %d: amplification must be between 0 and %d\n",
slouken@782
   325
		    name, line, MAX_AMPLIFICATION));
slouken@782
   326
	    goto fail;
slouken@782
   327
	  }
slouken@782
   328
	  bank->tone[i].amp=k;
slouken@782
   329
	}
slouken@782
   330
	else if (!strcmp(w[j], "note"))
slouken@782
   331
	{
slouken@782
   332
	  k=atoi(cp);
slouken@782
   333
	  if ((k<0 || k>127) || (*cp < '0' || *cp > '9'))
slouken@782
   334
	  {
slouken@782
   335
	    SNDDBG(("%s: line %d: note must be between 0 and 127\n",
slouken@782
   336
		    name, line));
slouken@782
   337
	    goto fail;
slouken@782
   338
	  }
slouken@782
   339
	  bank->tone[i].note=k;
slouken@782
   340
	}
slouken@782
   341
	else if (!strcmp(w[j], "pan"))
slouken@782
   342
	{
slouken@782
   343
	  if (!strcmp(cp, "center"))
slouken@782
   344
	    k=64;
slouken@782
   345
	  else if (!strcmp(cp, "left"))
slouken@782
   346
	    k=0;
slouken@782
   347
	  else if (!strcmp(cp, "right"))
slouken@782
   348
	    k=127;
slouken@782
   349
	  else
slouken@782
   350
	    k=((atoi(cp)+100) * 100) / 157;
slouken@782
   351
	  if ((k<0 || k>127) || (k==0 && *cp!='-' && (*cp < '0' || *cp > '9')))
slouken@782
   352
	  {
slouken@782
   353
	    SNDDBG(("%s: line %d: panning must be left, right, center, or between -100 and 100\n",
slouken@782
   354
		    name, line));
slouken@782
   355
	    goto fail;
slouken@782
   356
	  }
slouken@782
   357
	  bank->tone[i].pan=k;
slouken@782
   358
	}
slouken@782
   359
	else if (!strcmp(w[j], "keep"))
slouken@782
   360
	{
slouken@782
   361
	  if (!strcmp(cp, "env"))
slouken@782
   362
	    bank->tone[i].strip_envelope=0;
slouken@782
   363
	  else if (!strcmp(cp, "loop"))
slouken@782
   364
	    bank->tone[i].strip_loop=0;
slouken@782
   365
	  else
slouken@782
   366
	  {
slouken@782
   367
	    SNDDBG(("%s: line %d: keep must be env or loop\n", name, line));
slouken@782
   368
	    goto fail;
slouken@782
   369
	  }
slouken@782
   370
	}
slouken@782
   371
	else if (!strcmp(w[j], "strip"))
slouken@782
   372
	{
slouken@782
   373
	  if (!strcmp(cp, "env"))
slouken@782
   374
	    bank->tone[i].strip_envelope=1;
slouken@782
   375
	  else if (!strcmp(cp, "loop"))
slouken@782
   376
	    bank->tone[i].strip_loop=1;
slouken@782
   377
	  else if (!strcmp(cp, "tail"))
slouken@782
   378
	    bank->tone[i].strip_tail=1;
slouken@782
   379
	  else
slouken@782
   380
	  {
slouken@782
   381
	    SNDDBG(("%s: line %d: strip must be env, loop, or tail\n",
slouken@782
   382
		    name, line));
slouken@782
   383
	    goto fail;
slouken@782
   384
	  }
slouken@782
   385
	}
slouken@782
   386
	else
slouken@782
   387
	{
slouken@782
   388
	  SNDDBG(("%s: line %d: bad patch option %s\n", name, line, w[j]));
slouken@782
   389
	  goto fail;
slouken@782
   390
	}
slouken@0
   391
      }
slouken@0
   392
    }
slouken@782
   393
  }
slouken@782
   394
  SDL_RWclose(rw);
slouken@782
   395
  return 0;
slouken@782
   396
fail:
slouken@782
   397
  SDL_RWclose(rw);
slouken@782
   398
  return -2;
slouken@782
   399
}
slouken@782
   400
slouken@782
   401
int Timidity_Init_NoConfig()
slouken@782
   402
{
slouken@782
   403
  /* Allocate memory for the standard tonebank and drumset */
slouken@782
   404
  master_tonebank[0] = safe_malloc(sizeof(ToneBank));
slouken@782
   405
  memset(master_tonebank[0], 0, sizeof(ToneBank));
slouken@782
   406
  master_tonebank[0]->tone = safe_malloc(128 * sizeof(ToneBankElement));
slouken@782
   407
  memset(master_tonebank[0]->tone, 0, 128 * sizeof(ToneBankElement));
slouken@782
   408
slouken@782
   409
  master_drumset[0] = safe_malloc(sizeof(ToneBank));
slouken@782
   410
  memset(master_drumset[0], 0, sizeof(ToneBank));
slouken@782
   411
  master_drumset[0]->tone = safe_malloc(128 * sizeof(ToneBankElement));
slouken@782
   412
  memset(master_drumset[0]->tone, 0, 128 * sizeof(ToneBankElement));
slouken@782
   413
slouken@0
   414
  return 0;
slouken@0
   415
}
slouken@0
   416
slouken@782
   417
int Timidity_Init()
slouken@0
   418
{
slouken@416
   419
  const char *env = getenv("TIMIDITY_CFG");
slouken@782
   420
slouken@782
   421
  /* !!! FIXME: This may be ugly, but slightly less so than requiring the
slouken@782
   422
   *            default search path to have only one element. I think.
slouken@782
   423
   *
slouken@782
   424
   *            We only need to include the likely locations for the config
slouken@782
   425
   *            file itself since that file should contain any other directory
slouken@782
   426
   *            that needs to be added to the search path.
slouken@782
   427
   */
slouken@782
   428
#ifdef DEFAULT_PATH
slouken@782
   429
    add_to_pathlist(DEFAULT_PATH);
slouken@782
   430
#endif
slouken@782
   431
#ifdef DEFAULT_PATH1
slouken@782
   432
    add_to_pathlist(DEFAULT_PATH1);
slouken@782
   433
#endif
slouken@782
   434
#ifdef DEFAULT_PATH2
slouken@782
   435
    add_to_pathlist(DEFAULT_PATH2);
slouken@782
   436
#endif
slouken@782
   437
#ifdef DEFAULT_PATH3
slouken@782
   438
    add_to_pathlist(DEFAULT_PATH3);
slouken@782
   439
#endif
slouken@782
   440
slouken@782
   441
  Timidity_Init_NoConfig();
slouken@782
   442
slouken@416
   443
  if (!env || read_config_file(env)<0) {
slouken@416
   444
    if (read_config_file(CONFIG_FILE)<0) {
slouken@416
   445
      if (read_config_file(CONFIG_FILE_ETC)<0) {
slouken@604
   446
        if (read_config_file(CONFIG_FILE_ETC_TIMIDITY_FREEPATS)<0) {
slouken@604
   447
          return(-1);
slouken@604
   448
        }
patmandin@271
   449
      }
patmandin@263
   450
    }
slouken@0
   451
  }
slouken@782
   452
  return 0;
slouken@782
   453
}
slouken@0
   454
slouken@782
   455
MidiSong *Timidity_LoadDLSSong(SDL_RWops *rw, DLS_Patches *patches, SDL_AudioSpec *audio)
slouken@782
   456
{
slouken@782
   457
  MidiSong *song;
slouken@782
   458
  int i;
slouken@245
   459
slouken@782
   460
  if (rw == NULL)
slouken@782
   461
      return NULL;
slouken@782
   462
  
slouken@782
   463
  /* Allocate memory for the song */
slouken@782
   464
  song = (MidiSong *)safe_malloc(sizeof(*song));
slouken@782
   465
  memset(song, 0, sizeof(*song));
slouken@782
   466
  song->patches = patches;
slouken@245
   467
slouken@782
   468
  for (i = 0; i < MAXBANK; i++)
slouken@782
   469
  {
slouken@782
   470
    if (master_tonebank[i])
slouken@782
   471
    {
slouken@782
   472
      song->tonebank[i] = safe_malloc(sizeof(ToneBank));
slouken@782
   473
      memset(song->tonebank[i], 0, sizeof(ToneBank));
slouken@782
   474
      song->tonebank[i]->tone = master_tonebank[i]->tone;
slouken@782
   475
    }
slouken@782
   476
    if (master_drumset[i])
slouken@782
   477
    {
slouken@782
   478
      song->drumset[i] = safe_malloc(sizeof(ToneBank));
slouken@782
   479
      memset(song->drumset[i], 0, sizeof(ToneBank));
slouken@782
   480
      song->drumset[i]->tone = master_drumset[i]->tone;
slouken@782
   481
    }
slouken@0
   482
  }
slouken@0
   483
slouken@782
   484
  song->amplification = DEFAULT_AMPLIFICATION;
slouken@782
   485
  song->voices = DEFAULT_VOICES;
slouken@782
   486
  song->drumchannels = DEFAULT_DRUMCHANNELS;
slouken@782
   487
slouken@782
   488
  song->rw = rw;
slouken@782
   489
slouken@782
   490
  song->rate = audio->freq;
slouken@782
   491
  song->encoding = 0;
slouken@782
   492
  if ((audio->format & 0xFF) == 16)
slouken@782
   493
      song->encoding |= PE_16BIT;
slouken@782
   494
  if (audio->format & 0x8000)
slouken@782
   495
      song->encoding |= PE_SIGNED;
slouken@782
   496
  if (audio->channels == 1)
slouken@782
   497
      song->encoding |= PE_MONO;
slouken@782
   498
  switch (audio->format) {
slouken@782
   499
      case AUDIO_S8:
slouken@782
   500
	  song->write = s32tos8;
slouken@782
   501
	  break;
slouken@782
   502
      case AUDIO_U8:
slouken@782
   503
	  song->write = s32tou8;
slouken@782
   504
	  break;
slouken@782
   505
      case AUDIO_S16LSB:
slouken@782
   506
	  song->write = s32tos16l;
slouken@782
   507
	  break;
slouken@782
   508
      case AUDIO_S16MSB:
slouken@782
   509
	  song->write = s32tos16b;
slouken@782
   510
	  break;
slouken@782
   511
      case AUDIO_U16LSB:
slouken@782
   512
	  song->write = s32tou16l;
slouken@782
   513
	  break;
slouken@782
   514
      default:
slouken@782
   515
	  SNDDBG(("Unsupported audio format"));
slouken@782
   516
	  song->write = s32tou16l;
slouken@782
   517
	  break;
slouken@0
   518
  }
slouken@782
   519
slouken@782
   520
  song->buffer_size = audio->samples;
slouken@782
   521
  song->resample_buffer = safe_malloc(audio->samples * sizeof(sample_t));
slouken@782
   522
  song->common_buffer = safe_malloc(audio->samples * 2 * sizeof(Sint32));
slouken@782
   523
  
slouken@782
   524
  song->control_ratio = audio->freq / CONTROLS_PER_SECOND;
slouken@782
   525
  if (song->control_ratio < 1)
slouken@782
   526
      song->control_ratio = 1;
slouken@782
   527
  else if (song->control_ratio > MAX_CONTROL_RATIO)
slouken@782
   528
      song->control_ratio = MAX_CONTROL_RATIO;
slouken@782
   529
slouken@782
   530
  song->lost_notes = 0;
slouken@782
   531
  song->cut_notes = 0;
slouken@782
   532
slouken@782
   533
  song->events = read_midi_file(song, &(song->groomed_event_count),
slouken@782
   534
      &song->samples);
slouken@782
   535
slouken@782
   536
  /* The RWops can safely be closed at this point, but let's make that the
slouken@782
   537
   * responsibility of the caller.
slouken@782
   538
   */
slouken@782
   539
  
slouken@782
   540
  /* Make sure everything is okay */
slouken@782
   541
  if (!song->events) {
slouken@782
   542
    free(song);
slouken@782
   543
    return(NULL);
slouken@782
   544
  }
slouken@782
   545
slouken@782
   546
  song->default_instrument = 0;
slouken@782
   547
  song->default_program = DEFAULT_PROGRAM;
slouken@782
   548
slouken@0
   549
  if (*def_instr_name)
slouken@782
   550
    set_default_instrument(song, def_instr_name);
slouken@782
   551
slouken@782
   552
  load_missing_instruments(song);
slouken@782
   553
slouken@782
   554
  return(song);
slouken@0
   555
}
slouken@0
   556
slouken@782
   557
MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio)
slouken@0
   558
{
slouken@782
   559
  return Timidity_LoadDLSSong(rw, NULL, audio);
slouken@0
   560
}
icculus@450
   561
slouken@782
   562
void Timidity_FreeSong(MidiSong *song)
slouken@782
   563
{
slouken@782
   564
  int i;
slouken@782
   565
slouken@782
   566
  free_instruments(song);
slouken@782
   567
slouken@782
   568
  for (i = 0; i < 128; i++)
slouken@782
   569
  {
slouken@782
   570
    if (song->tonebank[i])
slouken@782
   571
      free(song->tonebank[i]);
slouken@782
   572
    if (song->drumset[i])
slouken@782
   573
      free(song->drumset[i]);
slouken@782
   574
  }
slouken@782
   575
  
slouken@782
   576
  free(song->common_buffer);
slouken@782
   577
  free(song->resample_buffer);
slouken@782
   578
  free(song->events);
slouken@782
   579
  free(song);
slouken@782
   580
}
slouken@782
   581
slouken@782
   582
void Timidity_Exit(void)
slouken@782
   583
{
slouken@782
   584
  int i, j;
slouken@782
   585
slouken@782
   586
  for (i = 0; i < MAXBANK; i++)
slouken@782
   587
  {
slouken@782
   588
    if (master_tonebank[i])
slouken@782
   589
    {
slouken@782
   590
      ToneBankElement *e = master_tonebank[i]->tone;
slouken@782
   591
      if (e != NULL)
slouken@782
   592
      {
slouken@782
   593
        for (j = 0; j < 128; j++)
slouken@782
   594
        {
slouken@782
   595
          if (e[j].name != NULL)
slouken@782
   596
            free(e[j].name);
slouken@782
   597
        }
slouken@782
   598
        free(e);
slouken@782
   599
      }
slouken@782
   600
      free(master_tonebank[i]);
slouken@782
   601
      master_tonebank[i] = NULL;
slouken@782
   602
    }
slouken@782
   603
    if (master_drumset[i])
slouken@782
   604
    {
slouken@782
   605
      ToneBankElement *e = master_drumset[i]->tone;
slouken@782
   606
      if (e != NULL)
slouken@782
   607
      {
slouken@782
   608
        for (j = 0; j < 128; j++)
slouken@782
   609
        {
slouken@782
   610
          if (e[j].name != NULL)
slouken@782
   611
            free(e[j].name);
slouken@782
   612
        }
slouken@782
   613
        free(e);
slouken@782
   614
      }
slouken@782
   615
      free(master_drumset[i]);
slouken@782
   616
      master_drumset[i] = NULL;
slouken@782
   617
    }
slouken@782
   618
  }
slouken@782
   619
slouken@782
   620
  free_pathlist();
slouken@782
   621
}