timidity/timidity.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 21 Oct 2017 03:15:57 -0700
changeset 811 d817ca30412d
parent 810 939986574bd8
child 828 561ae6f09324
permissions -rw-r--r--
Added Timidity support for 32-bit signed and float audio formats
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
sezero@799
   455
MidiSong *Timidity_LoadSong(SDL_RWops *rw, 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@245
   466
slouken@782
   467
  for (i = 0; i < MAXBANK; i++)
slouken@782
   468
  {
slouken@782
   469
    if (master_tonebank[i])
slouken@782
   470
    {
slouken@782
   471
      song->tonebank[i] = safe_malloc(sizeof(ToneBank));
slouken@782
   472
      memset(song->tonebank[i], 0, sizeof(ToneBank));
slouken@782
   473
      song->tonebank[i]->tone = master_tonebank[i]->tone;
slouken@782
   474
    }
slouken@782
   475
    if (master_drumset[i])
slouken@782
   476
    {
slouken@782
   477
      song->drumset[i] = safe_malloc(sizeof(ToneBank));
slouken@782
   478
      memset(song->drumset[i], 0, sizeof(ToneBank));
slouken@782
   479
      song->drumset[i]->tone = master_drumset[i]->tone;
slouken@782
   480
    }
slouken@0
   481
  }
slouken@0
   482
slouken@782
   483
  song->amplification = DEFAULT_AMPLIFICATION;
slouken@782
   484
  song->voices = DEFAULT_VOICES;
slouken@782
   485
  song->drumchannels = DEFAULT_DRUMCHANNELS;
slouken@782
   486
slouken@782
   487
  song->rw = rw;
slouken@782
   488
slouken@782
   489
  song->rate = audio->freq;
slouken@782
   490
  song->encoding = 0;
slouken@782
   491
  if ((audio->format & 0xFF) == 16)
slouken@782
   492
      song->encoding |= PE_16BIT;
slouken@811
   493
  else if ((audio->format & 0xFF) == 32)
slouken@811
   494
      song->encoding |= PE_32BIT;
slouken@782
   495
  if (audio->format & 0x8000)
slouken@782
   496
      song->encoding |= PE_SIGNED;
slouken@782
   497
  if (audio->channels == 1)
slouken@782
   498
      song->encoding |= PE_MONO;
slouken@803
   499
  else if (audio->channels > 2) {
slouken@803
   500
      SDL_SetError("Surround sound not supported");
slouken@803
   501
      return NULL;
slouken@803
   502
  }
slouken@782
   503
  switch (audio->format) {
slouken@803
   504
  case AUDIO_S8:
slouken@782
   505
	  song->write = s32tos8;
slouken@782
   506
	  break;
slouken@803
   507
  case AUDIO_U8:
slouken@782
   508
	  song->write = s32tou8;
slouken@782
   509
	  break;
slouken@803
   510
  case AUDIO_S16LSB:
slouken@782
   511
	  song->write = s32tos16l;
slouken@782
   512
	  break;
slouken@803
   513
  case AUDIO_S16MSB:
slouken@782
   514
	  song->write = s32tos16b;
slouken@782
   515
	  break;
slouken@803
   516
  case AUDIO_U16LSB:
slouken@782
   517
	  song->write = s32tou16l;
slouken@782
   518
	  break;
slouken@810
   519
  case AUDIO_U16MSB:
slouken@810
   520
	  song->write = s32tou16b;
slouken@810
   521
	  break;
slouken@811
   522
  case AUDIO_S32LSB:
slouken@811
   523
	  song->write = s32tos32l;
slouken@811
   524
	  break;
slouken@811
   525
  case AUDIO_S32MSB:
slouken@811
   526
	  song->write = s32tos32b;
slouken@811
   527
	  break;
slouken@811
   528
  case AUDIO_F32SYS:
slouken@811
   529
	  song->write = s32tof32;
slouken@811
   530
	  break;
slouken@803
   531
  default:
slouken@803
   532
	  SDL_SetError("Unsupported audio format");
slouken@803
   533
      return NULL;
slouken@0
   534
  }
slouken@782
   535
slouken@782
   536
  song->buffer_size = audio->samples;
slouken@782
   537
  song->resample_buffer = safe_malloc(audio->samples * sizeof(sample_t));
slouken@782
   538
  song->common_buffer = safe_malloc(audio->samples * 2 * sizeof(Sint32));
slouken@782
   539
  
slouken@782
   540
  song->control_ratio = audio->freq / CONTROLS_PER_SECOND;
slouken@782
   541
  if (song->control_ratio < 1)
slouken@782
   542
      song->control_ratio = 1;
slouken@782
   543
  else if (song->control_ratio > MAX_CONTROL_RATIO)
slouken@782
   544
      song->control_ratio = MAX_CONTROL_RATIO;
slouken@782
   545
slouken@782
   546
  song->lost_notes = 0;
slouken@782
   547
  song->cut_notes = 0;
slouken@782
   548
slouken@782
   549
  song->events = read_midi_file(song, &(song->groomed_event_count),
slouken@782
   550
      &song->samples);
slouken@782
   551
slouken@782
   552
  /* The RWops can safely be closed at this point, but let's make that the
slouken@782
   553
   * responsibility of the caller.
slouken@782
   554
   */
slouken@782
   555
  
slouken@782
   556
  /* Make sure everything is okay */
slouken@782
   557
  if (!song->events) {
slouken@782
   558
    free(song);
slouken@782
   559
    return(NULL);
slouken@782
   560
  }
slouken@782
   561
slouken@782
   562
  song->default_instrument = 0;
slouken@782
   563
  song->default_program = DEFAULT_PROGRAM;
slouken@782
   564
slouken@0
   565
  if (*def_instr_name)
slouken@782
   566
    set_default_instrument(song, def_instr_name);
slouken@782
   567
slouken@782
   568
  load_missing_instruments(song);
slouken@782
   569
slouken@782
   570
  return(song);
slouken@0
   571
}
slouken@0
   572
slouken@782
   573
void Timidity_FreeSong(MidiSong *song)
slouken@782
   574
{
slouken@782
   575
  int i;
slouken@782
   576
slouken@782
   577
  free_instruments(song);
slouken@782
   578
slouken@782
   579
  for (i = 0; i < 128; i++)
slouken@782
   580
  {
slouken@782
   581
    if (song->tonebank[i])
slouken@782
   582
      free(song->tonebank[i]);
slouken@782
   583
    if (song->drumset[i])
slouken@782
   584
      free(song->drumset[i]);
slouken@782
   585
  }
slouken@782
   586
  
slouken@782
   587
  free(song->common_buffer);
slouken@782
   588
  free(song->resample_buffer);
slouken@782
   589
  free(song->events);
slouken@782
   590
  free(song);
slouken@782
   591
}
slouken@782
   592
slouken@782
   593
void Timidity_Exit(void)
slouken@782
   594
{
slouken@782
   595
  int i, j;
slouken@782
   596
slouken@782
   597
  for (i = 0; i < MAXBANK; i++)
slouken@782
   598
  {
slouken@782
   599
    if (master_tonebank[i])
slouken@782
   600
    {
slouken@782
   601
      ToneBankElement *e = master_tonebank[i]->tone;
slouken@782
   602
      if (e != NULL)
slouken@782
   603
      {
slouken@782
   604
        for (j = 0; j < 128; j++)
slouken@782
   605
        {
slouken@782
   606
          if (e[j].name != NULL)
slouken@782
   607
            free(e[j].name);
slouken@782
   608
        }
slouken@782
   609
        free(e);
slouken@782
   610
      }
slouken@782
   611
      free(master_tonebank[i]);
slouken@782
   612
      master_tonebank[i] = NULL;
slouken@782
   613
    }
slouken@782
   614
    if (master_drumset[i])
slouken@782
   615
    {
slouken@782
   616
      ToneBankElement *e = master_drumset[i]->tone;
slouken@782
   617
      if (e != NULL)
slouken@782
   618
      {
slouken@782
   619
        for (j = 0; j < 128; j++)
slouken@782
   620
        {
slouken@782
   621
          if (e[j].name != NULL)
slouken@782
   622
            free(e[j].name);
slouken@782
   623
        }
slouken@782
   624
        free(e);
slouken@782
   625
      }
slouken@782
   626
      free(master_drumset[i]);
slouken@782
   627
      master_drumset[i] = NULL;
slouken@782
   628
    }
slouken@782
   629
  }
slouken@782
   630
slouken@782
   631
  free_pathlist();
slouken@782
   632
}