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