timidity/playmidi.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 12 Nov 2018 16:54:24 -0800
changeset 925 5945988b4a41
parent 811 d817ca30412d
permissions -rw-r--r--
Fixed bug 4371 - tvOS Simulator devices not listed

Caleb Cornett

In the Xcode-iOS project, when selecting the libSDL_mixer-tvOS target, no tvOS simulators appear in the available device dropdown.

This is easily fixed with the attached patch.
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@0
     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
    playmidi.c -- random stuff in need of rearrangement
slouken@782
    10
slouken@782
    11
*/
slouken@782
    12
slouken@782
    13
#if HAVE_CONFIG_H
slouken@782
    14
#  include <config.h>
slouken@782
    15
#endif
slouken@0
    16
slouken@0
    17
#include <stdio.h>
slouken@0
    18
#include <stdlib.h>
slouken@0
    19
#include <string.h>
slouken@0
    20
slouken@782
    21
#include "SDL.h"
patmandin@265
    22
slouken@782
    23
#include "timidity.h"
slouken@782
    24
#include "options.h"
slouken@0
    25
#include "instrum.h"
slouken@0
    26
#include "playmidi.h"
slouken@0
    27
#include "output.h"
slouken@0
    28
#include "mix.h"
slouken@0
    29
#include "tables.h"
slouken@0
    30
slouken@782
    31
static void adjust_amplification(MidiSong *song)
slouken@0
    32
{ 
slouken@782
    33
  song->master_volume = (float)(song->amplification) / (float)100.0;
slouken@0
    34
}
slouken@0
    35
slouken@782
    36
static void reset_voices(MidiSong *song)
slouken@0
    37
{
slouken@0
    38
  int i;
slouken@0
    39
  for (i=0; i<MAX_VOICES; i++)
slouken@782
    40
    song->voice[i].status=VOICE_FREE;
slouken@0
    41
}
slouken@0
    42
slouken@0
    43
/* Process the Reset All Controllers event */
slouken@782
    44
static void reset_controllers(MidiSong *song, int c)
slouken@0
    45
{
slouken@782
    46
  song->channel[c].volume=90; /* Some standard says, although the SCC docs say 0. */
slouken@782
    47
  song->channel[c].expression=127; /* SCC-1 does this. */
slouken@782
    48
  song->channel[c].sustain=0;
slouken@782
    49
  song->channel[c].pitchbend=0x2000;
slouken@782
    50
  song->channel[c].pitchfactor=0; /* to be computed */
slouken@0
    51
}
slouken@0
    52
slouken@782
    53
static void reset_midi(MidiSong *song)
slouken@0
    54
{
slouken@0
    55
  int i;
slouken@245
    56
  for (i=0; i<MAXCHAN; i++)
slouken@0
    57
    {
slouken@782
    58
      reset_controllers(song, i);
slouken@0
    59
      /* The rest of these are unaffected by the Reset All Controllers event */
slouken@782
    60
      song->channel[i].program=song->default_program;
slouken@782
    61
      song->channel[i].panning=NO_PANNING;
slouken@782
    62
      song->channel[i].pitchsens=2;
slouken@782
    63
      song->channel[i].bank=0; /* tone bank or drum set */
slouken@0
    64
    }
slouken@782
    65
  reset_voices(song);
slouken@0
    66
}
slouken@0
    67
slouken@782
    68
static void select_sample(MidiSong *song, int v, Instrument *ip, int vel)
slouken@0
    69
{
slouken@782
    70
  Sint32 f, cdiff, diff;
slouken@0
    71
  int s,i;
slouken@0
    72
  Sample *sp, *closest;
slouken@0
    73
slouken@0
    74
  s=ip->samples;
slouken@0
    75
  sp=ip->sample;
slouken@0
    76
slouken@0
    77
  if (s==1)
slouken@0
    78
    {
slouken@782
    79
      song->voice[v].sample=sp;
slouken@0
    80
      return;
slouken@0
    81
    }
slouken@0
    82
slouken@782
    83
  f=song->voice[v].orig_frequency;
slouken@782
    84
  for (i=0; i<s; i++)
slouken@782
    85
    {
sezero@799
    86
      if (sp->low_freq <= f && sp->high_freq >= f)
slouken@782
    87
	{
slouken@782
    88
	  song->voice[v].sample=sp;
slouken@782
    89
	  return;
slouken@782
    90
	}
slouken@782
    91
      sp++;
slouken@782
    92
    }
slouken@782
    93
slouken@0
    94
  /* 
slouken@0
    95
     No suitable sample found! We'll select the sample whose root
slouken@0
    96
     frequency is closest to the one we want. (Actually we should
slouken@0
    97
     probably convert the low, high, and root frequencies to MIDI note
slouken@0
    98
     values and compare those.) */
slouken@0
    99
slouken@0
   100
  cdiff=0x7FFFFFFF;
slouken@0
   101
  closest=sp=ip->sample;
slouken@0
   102
  for(i=0; i<s; i++)
slouken@0
   103
    {
slouken@0
   104
      diff=sp->root_freq - f;
slouken@0
   105
      if (diff<0) diff=-diff;
slouken@0
   106
      if (diff<cdiff)
slouken@0
   107
	{
slouken@0
   108
	  cdiff=diff;
slouken@0
   109
	  closest=sp;
slouken@0
   110
	}
slouken@0
   111
      sp++;
slouken@0
   112
    }
slouken@782
   113
  song->voice[v].sample=closest;
slouken@0
   114
  return;
slouken@0
   115
}
slouken@0
   116
slouken@782
   117
static void recompute_freq(MidiSong *song, int v)
slouken@0
   118
{
slouken@0
   119
  int 
slouken@782
   120
    sign=(song->voice[v].sample_increment < 0), /* for bidirectional loops */
slouken@782
   121
    pb=song->channel[song->voice[v].channel].pitchbend;
slouken@0
   122
  double a;
slouken@0
   123
  
slouken@782
   124
  if (!song->voice[v].sample->sample_rate)
slouken@0
   125
    return;
slouken@0
   126
slouken@782
   127
  if (song->voice[v].vibrato_control_ratio)
slouken@0
   128
    {
slouken@0
   129
      /* This instrument has vibrato. Invalidate any precomputed
slouken@0
   130
         sample_increments. */
slouken@0
   131
slouken@0
   132
      int i=VIBRATO_SAMPLE_INCREMENTS;
slouken@0
   133
      while (i--)
slouken@782
   134
	song->voice[v].vibrato_sample_increment[i]=0;
slouken@0
   135
    }
slouken@0
   136
slouken@0
   137
  if (pb==0x2000 || pb<0 || pb>0x3FFF)
slouken@782
   138
    song->voice[v].frequency = song->voice[v].orig_frequency;
slouken@0
   139
  else
slouken@0
   140
    {
slouken@0
   141
      pb-=0x2000;
slouken@782
   142
      if (!(song->channel[song->voice[v].channel].pitchfactor))
slouken@0
   143
	{
slouken@0
   144
	  /* Damn. Somebody bent the pitch. */
slouken@782
   145
	  Sint32 i=pb*song->channel[song->voice[v].channel].pitchsens;
slouken@0
   146
	  if (pb<0)
slouken@0
   147
	    i=-i;
slouken@782
   148
	  song->channel[song->voice[v].channel].pitchfactor=
slouken@782
   149
	    (float)(bend_fine[(i>>5) & 0xFF] * bend_coarse[i>>13]);
slouken@0
   150
	}
slouken@0
   151
      if (pb>0)
slouken@782
   152
	song->voice[v].frequency=
slouken@782
   153
	  (Sint32)(song->channel[song->voice[v].channel].pitchfactor *
slouken@782
   154
		  (double)(song->voice[v].orig_frequency));
slouken@0
   155
      else
slouken@782
   156
	song->voice[v].frequency=
slouken@782
   157
	  (Sint32)((double)(song->voice[v].orig_frequency) /
slouken@782
   158
		  song->channel[song->voice[v].channel].pitchfactor);
slouken@0
   159
    }
slouken@0
   160
slouken@782
   161
  a = FSCALE(((double)(song->voice[v].sample->sample_rate) *
slouken@782
   162
	      (double)(song->voice[v].frequency)) /
slouken@782
   163
	     ((double)(song->voice[v].sample->root_freq) *
slouken@782
   164
	      (double)(song->rate)),
slouken@0
   165
	     FRACTION_BITS);
slouken@0
   166
slouken@0
   167
  if (sign) 
slouken@0
   168
    a = -a; /* need to preserve the loop direction */
slouken@0
   169
slouken@782
   170
  song->voice[v].sample_increment = (Sint32)(a);
slouken@0
   171
}
slouken@0
   172
slouken@782
   173
static void recompute_amp(MidiSong *song, int v)
slouken@245
   174
{
slouken@782
   175
  Sint32 tempamp;
slouken@0
   176
slouken@0
   177
  /* TODO: use fscale */
slouken@0
   178
slouken@782
   179
  tempamp= (song->voice[v].velocity *
slouken@782
   180
	    song->channel[song->voice[v].channel].volume * 
slouken@782
   181
	    song->channel[song->voice[v].channel].expression); /* 21 bits */
slouken@782
   182
slouken@782
   183
  if (!(song->encoding & PE_MONO))
slouken@0
   184
    {
slouken@782
   185
      if (song->voice[v].panning > 60 && song->voice[v].panning < 68)
slouken@0
   186
	{
slouken@782
   187
	  song->voice[v].panned=PANNED_CENTER;
slouken@0
   188
slouken@782
   189
	  song->voice[v].left_amp=
slouken@782
   190
	    FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
slouken@782
   191
		      21);
slouken@0
   192
	}
slouken@782
   193
      else if (song->voice[v].panning<5)
slouken@0
   194
	{
slouken@782
   195
	  song->voice[v].panned = PANNED_LEFT;
slouken@0
   196
slouken@782
   197
	  song->voice[v].left_amp=
slouken@782
   198
	    FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
slouken@0
   199
		      20);
slouken@0
   200
	}
slouken@782
   201
      else if (song->voice[v].panning>123)
slouken@0
   202
	{
slouken@782
   203
	  song->voice[v].panned = PANNED_RIGHT;
slouken@0
   204
slouken@782
   205
	  song->voice[v].left_amp= /* left_amp will be used */
slouken@782
   206
	    FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
slouken@0
   207
		      20);
slouken@0
   208
	}
slouken@0
   209
      else
slouken@0
   210
	{
slouken@782
   211
	  song->voice[v].panned = PANNED_MYSTERY;
slouken@245
   212
slouken@782
   213
	  song->voice[v].left_amp=
slouken@782
   214
	    FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
slouken@782
   215
		      27);
slouken@782
   216
	  song->voice[v].right_amp = song->voice[v].left_amp * (song->voice[v].panning);
slouken@782
   217
	  song->voice[v].left_amp *= (float)(127 - song->voice[v].panning);
slouken@0
   218
	}
slouken@0
   219
    }
slouken@0
   220
  else
slouken@0
   221
    {
slouken@782
   222
      song->voice[v].panned = PANNED_CENTER;
slouken@0
   223
slouken@782
   224
      song->voice[v].left_amp=
slouken@782
   225
	FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
slouken@0
   226
		  21);
slouken@0
   227
    }
slouken@0
   228
}
slouken@0
   229
slouken@782
   230
static void start_note(MidiSong *song, MidiEvent *e, int i)
slouken@782
   231
{
slouken@782
   232
  Instrument *ip;
slouken@782
   233
  int j;
slouken@245
   234
slouken@782
   235
  if (ISDRUMCHANNEL(song, e->channel))
slouken@782
   236
    {
slouken@782
   237
      if (!(ip=song->drumset[song->channel[e->channel].bank]->instrument[e->a]))
slouken@782
   238
	{
slouken@782
   239
	  if (!(ip=song->drumset[0]->instrument[e->a]))
slouken@782
   240
	    return; /* No instrument? Then we can't play. */
slouken@782
   241
	}
slouken@782
   242
      if (ip->samples != 1)
slouken@782
   243
	{
slouken@782
   244
	  SNDDBG(("Strange: percussion instrument with %d samples!",
slouken@782
   245
		  ip->samples));
slouken@782
   246
	}
slouken@245
   247
slouken@782
   248
      if (ip->sample->note_to_use) /* Do we have a fixed pitch? */
slouken@782
   249
	song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
slouken@782
   250
      else
slouken@782
   251
	song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
slouken@782
   252
      
slouken@782
   253
      /* drums are supposed to have only one sample */
slouken@782
   254
      song->voice[i].sample = ip->sample;
slouken@782
   255
    }
slouken@782
   256
  else
slouken@782
   257
    {
slouken@782
   258
      if (song->channel[e->channel].program == SPECIAL_PROGRAM)
slouken@782
   259
	ip=song->default_instrument;
slouken@782
   260
      else if (!(ip=song->tonebank[song->channel[e->channel].bank]->
slouken@782
   261
		 instrument[song->channel[e->channel].program]))
slouken@782
   262
	{
slouken@782
   263
	  if (!(ip=song->tonebank[0]->instrument[song->channel[e->channel].program]))
slouken@782
   264
	    return; /* No instrument? Then we can't play. */
slouken@782
   265
	}
slouken@245
   266
slouken@782
   267
      if (ip->sample->note_to_use) /* Fixed-pitch instrument? */
slouken@782
   268
	song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
slouken@782
   269
      else
slouken@782
   270
	song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
slouken@782
   271
      select_sample(song, i, ip, e->b);
slouken@782
   272
    }
slouken@782
   273
slouken@782
   274
  song->voice[i].status = VOICE_ON;
slouken@782
   275
  song->voice[i].channel = e->channel;
slouken@782
   276
  song->voice[i].note = e->a;
slouken@782
   277
  song->voice[i].velocity = e->b;
slouken@782
   278
  song->voice[i].sample_offset = 0;
slouken@782
   279
  song->voice[i].sample_increment = 0; /* make sure it isn't negative */
slouken@782
   280
slouken@782
   281
  song->voice[i].tremolo_phase = 0;
slouken@782
   282
  song->voice[i].tremolo_phase_increment = song->voice[i].sample->tremolo_phase_increment;
slouken@782
   283
  song->voice[i].tremolo_sweep = song->voice[i].sample->tremolo_sweep_increment;
slouken@782
   284
  song->voice[i].tremolo_sweep_position = 0;
slouken@782
   285
slouken@782
   286
  song->voice[i].vibrato_sweep = song->voice[i].sample->vibrato_sweep_increment;
slouken@782
   287
  song->voice[i].vibrato_sweep_position = 0;
slouken@782
   288
  song->voice[i].vibrato_control_ratio = song->voice[i].sample->vibrato_control_ratio;
slouken@782
   289
  song->voice[i].vibrato_control_counter = song->voice[i].vibrato_phase = 0;
slouken@782
   290
  for (j=0; j<VIBRATO_SAMPLE_INCREMENTS; j++)
slouken@782
   291
    song->voice[i].vibrato_sample_increment[j] = 0;
slouken@782
   292
slouken@782
   293
  if (song->channel[e->channel].panning != NO_PANNING)
slouken@782
   294
    song->voice[i].panning = song->channel[e->channel].panning;
slouken@782
   295
  else
slouken@782
   296
    song->voice[i].panning = song->voice[i].sample->panning;
slouken@782
   297
slouken@782
   298
  recompute_freq(song, i);
slouken@782
   299
  recompute_amp(song, i);
slouken@782
   300
  if (song->voice[i].sample->modes & MODES_ENVELOPE)
slouken@782
   301
    {
slouken@782
   302
      /* Ramp up from 0 */
slouken@782
   303
      song->voice[i].envelope_stage = 0;
slouken@782
   304
      song->voice[i].envelope_volume = 0;
slouken@782
   305
      song->voice[i].control_counter = 0;
slouken@782
   306
      recompute_envelope(song, i);
slouken@782
   307
      apply_envelope_to_amp(song, i);
slouken@782
   308
    }
slouken@782
   309
  else
slouken@782
   310
    {
slouken@782
   311
      song->voice[i].envelope_increment = 0;
slouken@782
   312
      apply_envelope_to_amp(song, i);
slouken@782
   313
    }
slouken@782
   314
}
slouken@782
   315
slouken@782
   316
static void kill_note(MidiSong *song, int i)
slouken@245
   317
{
slouken@782
   318
  song->voice[i].status = VOICE_DIE;
slouken@782
   319
}
slouken@782
   320
slouken@782
   321
/* Only one instance of a note can be playing on a single channel. */
slouken@782
   322
static void note_on(MidiSong *song)
slouken@782
   323
{
slouken@782
   324
  int i = song->voices, lowest=-1; 
slouken@782
   325
  Sint32 lv=0x7FFFFFFF, v;
slouken@782
   326
  MidiEvent *e = song->current_event;
slouken@245
   327
slouken@245
   328
  while (i--)
slouken@245
   329
    {
slouken@782
   330
      if (song->voice[i].status == VOICE_FREE)
slouken@0
   331
	lowest=i; /* Can't get a lower volume than silence */
slouken@782
   332
      else if (song->voice[i].channel==e->channel && 
slouken@782
   333
	       (song->voice[i].note==e->a || song->channel[song->voice[i].channel].mono))
slouken@782
   334
	kill_note(song, i);
slouken@0
   335
    }
slouken@0
   336
slouken@0
   337
  if (lowest != -1)
slouken@0
   338
    {
slouken@0
   339
      /* Found a free voice. */
slouken@782
   340
      start_note(song,e,lowest);
slouken@0
   341
      return;
slouken@0
   342
    }
slouken@0
   343
  
slouken@0
   344
  /* Look for the decaying note with the lowest volume */
slouken@782
   345
  i = song->voices;
slouken@0
   346
  while (i--)
slouken@0
   347
    {
slouken@782
   348
      if ((song->voice[i].status != VOICE_ON) &&
slouken@782
   349
	  (song->voice[i].status != VOICE_DIE))
slouken@0
   350
	{
slouken@782
   351
	  v = song->voice[i].left_mix;
slouken@782
   352
	  if ((song->voice[i].panned == PANNED_MYSTERY)
slouken@782
   353
	      && (song->voice[i].right_mix > v))
slouken@782
   354
	    v = song->voice[i].right_mix;
slouken@0
   355
	  if (v<lv)
slouken@0
   356
	    {
slouken@0
   357
	      lv=v;
slouken@0
   358
	      lowest=i;
slouken@0
   359
	    }
slouken@0
   360
	}
slouken@0
   361
    }
slouken@0
   362
slouken@0
   363
  if (lowest != -1)
slouken@0
   364
    {
slouken@0
   365
      /* This can still cause a click, but if we had a free voice to
slouken@0
   366
	 spare for ramping down this note, we wouldn't need to kill it
slouken@0
   367
	 in the first place... Still, this needs to be fixed. Perhaps
slouken@0
   368
	 we could use a reserve of voices to play dying notes only. */
slouken@782
   369
      
slouken@782
   370
      song->cut_notes++;
slouken@782
   371
      song->voice[lowest].status=VOICE_FREE;
slouken@782
   372
      start_note(song,e,lowest);
slouken@0
   373
    }
slouken@0
   374
  else
slouken@782
   375
    song->lost_notes++;
slouken@0
   376
}
slouken@0
   377
slouken@782
   378
static void finish_note(MidiSong *song, int i)
slouken@0
   379
{
slouken@782
   380
  if (song->voice[i].sample->modes & MODES_ENVELOPE)
slouken@0
   381
    {
slouken@0
   382
      /* We need to get the envelope out of Sustain stage */
slouken@782
   383
      song->voice[i].envelope_stage = 3;
slouken@782
   384
      song->voice[i].status = VOICE_OFF;
slouken@782
   385
      recompute_envelope(song, i);
slouken@782
   386
      apply_envelope_to_amp(song, i);
slouken@0
   387
    }
slouken@0
   388
  else
slouken@0
   389
    {
slouken@0
   390
      /* Set status to OFF so resample_voice() will let this voice out
slouken@0
   391
         of its loop, if any. In any case, this voice dies when it
slouken@0
   392
         hits the end of its data (ofs>=data_length). */
slouken@782
   393
      song->voice[i].status = VOICE_OFF;
slouken@0
   394
    }
slouken@0
   395
}
slouken@0
   396
slouken@782
   397
static void note_off(MidiSong *song)
slouken@0
   398
{
slouken@782
   399
  int i = song->voices;
slouken@782
   400
  MidiEvent *e = song->current_event;
slouken@782
   401
slouken@0
   402
  while (i--)
slouken@782
   403
    if (song->voice[i].status == VOICE_ON &&
slouken@782
   404
	song->voice[i].channel == e->channel &&
slouken@782
   405
	song->voice[i].note == e->a)
slouken@0
   406
      {
slouken@782
   407
	if (song->channel[e->channel].sustain)
slouken@0
   408
	  {
slouken@782
   409
	    song->voice[i].status = VOICE_SUSTAINED;
slouken@0
   410
	  }
slouken@0
   411
	else
slouken@782
   412
	  finish_note(song, i);
slouken@0
   413
	return;
slouken@0
   414
      }
slouken@0
   415
}
slouken@0
   416
slouken@0
   417
/* Process the All Notes Off event */
slouken@782
   418
static void all_notes_off(MidiSong *song)
slouken@0
   419
{
slouken@782
   420
  int i = song->voices;
slouken@782
   421
  int c = song->current_event->channel;
slouken@782
   422
slouken@782
   423
  SNDDBG(("All notes off on channel %d", c));
slouken@0
   424
  while (i--)
slouken@782
   425
    if (song->voice[i].status == VOICE_ON &&
slouken@782
   426
	song->voice[i].channel == c)
slouken@0
   427
      {
slouken@782
   428
	if (song->channel[c].sustain) 
slouken@782
   429
	  song->voice[i].status = VOICE_SUSTAINED;
slouken@0
   430
	else
slouken@782
   431
	  finish_note(song, i);
slouken@0
   432
      }
slouken@0
   433
}
slouken@0
   434
slouken@0
   435
/* Process the All Sounds Off event */
slouken@782
   436
static void all_sounds_off(MidiSong *song)
slouken@0
   437
{
slouken@782
   438
  int i = song->voices;
slouken@782
   439
  int c = song->current_event->channel;
slouken@782
   440
slouken@0
   441
  while (i--)
slouken@782
   442
    if (song->voice[i].channel == c && 
slouken@782
   443
	song->voice[i].status != VOICE_FREE &&
slouken@782
   444
	song->voice[i].status != VOICE_DIE)
slouken@0
   445
      {
slouken@782
   446
	kill_note(song, i);
slouken@0
   447
      }
slouken@0
   448
}
slouken@0
   449
slouken@782
   450
static void adjust_pressure(MidiSong *song)
slouken@0
   451
{
slouken@782
   452
  MidiEvent *e = song->current_event;
slouken@782
   453
  int i = song->voices;
slouken@782
   454
  
slouken@0
   455
  while (i--)
slouken@782
   456
    if (song->voice[i].status == VOICE_ON &&
slouken@782
   457
	song->voice[i].channel == e->channel &&
slouken@782
   458
	song->voice[i].note == e->a)
slouken@0
   459
      {
slouken@782
   460
	song->voice[i].velocity = e->b;
slouken@782
   461
	recompute_amp(song, i);
slouken@782
   462
	apply_envelope_to_amp(song, i);
slouken@0
   463
	return;
slouken@0
   464
      }
slouken@0
   465
}
slouken@0
   466
slouken@782
   467
static void drop_sustain(MidiSong *song)
slouken@0
   468
{
slouken@782
   469
  int i = song->voices;
slouken@782
   470
  int c = song->current_event->channel;
slouken@782
   471
slouken@0
   472
  while (i--)
slouken@782
   473
    if (song->voice[i].status == VOICE_SUSTAINED && song->voice[i].channel == c)
slouken@782
   474
      finish_note(song, i);
slouken@782
   475
}
slouken@782
   476
slouken@782
   477
static void adjust_pitchbend(MidiSong *song)
slouken@782
   478
{
slouken@782
   479
  int c = song->current_event->channel;
slouken@782
   480
  int i = song->voices;
slouken@782
   481
  
slouken@782
   482
  while (i--)
slouken@782
   483
    if (song->voice[i].status != VOICE_FREE && song->voice[i].channel == c)
slouken@0
   484
      {
slouken@782
   485
	recompute_freq(song, i);
slouken@0
   486
      }
slouken@0
   487
}
slouken@0
   488
slouken@782
   489
static void adjust_volume(MidiSong *song)
slouken@0
   490
{
slouken@782
   491
  int c = song->current_event->channel;
slouken@782
   492
  int i = song->voices;
slouken@782
   493
slouken@0
   494
  while (i--)
slouken@782
   495
    if (song->voice[i].channel == c &&
slouken@782
   496
	(song->voice[i].status==VOICE_ON || song->voice[i].status==VOICE_SUSTAINED))
slouken@0
   497
      {
slouken@782
   498
	recompute_amp(song, i);
slouken@782
   499
	apply_envelope_to_amp(song, i);
slouken@0
   500
      }
slouken@0
   501
}
slouken@0
   502
slouken@782
   503
static void seek_forward(MidiSong *song, Sint32 until_time)
slouken@0
   504
{
slouken@782
   505
  reset_voices(song);
slouken@782
   506
  while (song->current_event->time < until_time)
slouken@0
   507
    {
slouken@782
   508
      switch(song->current_event->type)
slouken@0
   509
	{
slouken@0
   510
	  /* All notes stay off. Just handle the parameter changes. */
slouken@0
   511
slouken@0
   512
	case ME_PITCH_SENS:
slouken@782
   513
	  song->channel[song->current_event->channel].pitchsens =
slouken@782
   514
	    song->current_event->a;
slouken@782
   515
	  song->channel[song->current_event->channel].pitchfactor = 0;
slouken@0
   516
	  break;
slouken@0
   517
	  
slouken@0
   518
	case ME_PITCHWHEEL:
slouken@782
   519
	  song->channel[song->current_event->channel].pitchbend =
slouken@782
   520
	    song->current_event->a + song->current_event->b * 128;
slouken@782
   521
	  song->channel[song->current_event->channel].pitchfactor = 0;
slouken@0
   522
	  break;
slouken@0
   523
	  
slouken@0
   524
	case ME_MAINVOLUME:
slouken@782
   525
	  song->channel[song->current_event->channel].volume =
slouken@782
   526
	    song->current_event->a;
slouken@245
   527
	  break;
slouken@245
   528
	  
slouken@0
   529
	case ME_PAN:
slouken@782
   530
	  song->channel[song->current_event->channel].panning =
slouken@782
   531
	    song->current_event->a;
slouken@0
   532
	  break;
slouken@0
   533
	      
slouken@0
   534
	case ME_EXPRESSION:
slouken@782
   535
	  song->channel[song->current_event->channel].expression =
slouken@782
   536
	    song->current_event->a;
slouken@0
   537
	  break;
slouken@0
   538
	  
slouken@0
   539
	case ME_PROGRAM:
slouken@782
   540
	  if (ISDRUMCHANNEL(song, song->current_event->channel))
slouken@0
   541
	    /* Change drum set */
slouken@782
   542
	    song->channel[song->current_event->channel].bank =
slouken@782
   543
	      song->current_event->a;
slouken@0
   544
	  else
slouken@782
   545
	    song->channel[song->current_event->channel].program =
slouken@782
   546
	      song->current_event->a;
slouken@0
   547
	  break;
slouken@0
   548
slouken@0
   549
	case ME_SUSTAIN:
slouken@782
   550
	  song->channel[song->current_event->channel].sustain =
slouken@782
   551
	    song->current_event->a;
slouken@0
   552
	  break;
slouken@0
   553
slouken@0
   554
	case ME_RESET_CONTROLLERS:
slouken@782
   555
	  reset_controllers(song, song->current_event->channel);
slouken@0
   556
	  break;
slouken@0
   557
	      
slouken@0
   558
	case ME_TONE_BANK:
slouken@782
   559
	  song->channel[song->current_event->channel].bank =
slouken@782
   560
	    song->current_event->a;
slouken@0
   561
	  break;
slouken@0
   562
	  
slouken@0
   563
	case ME_EOT:
slouken@782
   564
	  song->current_sample = song->current_event->time;
slouken@0
   565
	  return;
slouken@0
   566
	}
slouken@782
   567
      song->current_event++;
slouken@0
   568
    }
slouken@782
   569
  /*song->current_sample=song->current_event->time;*/
slouken@782
   570
  if (song->current_event != song->events)
slouken@782
   571
    song->current_event--;
slouken@782
   572
  song->current_sample=until_time;
slouken@0
   573
}
slouken@0
   574
slouken@782
   575
static void skip_to(MidiSong *song, Sint32 until_time)
slouken@0
   576
{
slouken@782
   577
  if (song->current_sample > until_time)
slouken@782
   578
    song->current_sample = 0;
slouken@0
   579
slouken@782
   580
  reset_midi(song);
slouken@782
   581
  song->buffered_count = 0;
slouken@782
   582
  song->buffer_pointer = song->common_buffer;
slouken@782
   583
  song->current_event = song->events;
slouken@0
   584
  
slouken@0
   585
  if (until_time)
slouken@782
   586
    seek_forward(song, until_time);
slouken@0
   587
}
slouken@0
   588
slouken@782
   589
static void do_compute_data(MidiSong *song, Sint32 count)
slouken@0
   590
{
slouken@0
   591
  int i;
slouken@782
   592
  memset(song->buffer_pointer, 0, 
slouken@782
   593
	 (song->encoding & PE_MONO) ? (count * 4) : (count * 8));
slouken@782
   594
  for (i = 0; i < song->voices; i++)
slouken@0
   595
    {
slouken@782
   596
      if(song->voice[i].status != VOICE_FREE)
slouken@782
   597
	mix_voice(song, song->buffer_pointer, i, count);
slouken@0
   598
    }
slouken@782
   599
  song->current_sample += count;
slouken@0
   600
}
slouken@0
   601
slouken@0
   602
/* count=0 means flush remaining buffered data to output device, then
slouken@0
   603
   flush the device itself */
slouken@782
   604
static void compute_data(MidiSong *song, void *stream, Sint32 count)
slouken@0
   605
{
slouken@782
   606
  int channels;
slouken@0
   607
slouken@782
   608
  if ( song->encoding & PE_MONO )
slouken@0
   609
    channels = 1;
slouken@0
   610
  else
slouken@782
   611
    channels = 2;
slouken@0
   612
slouken@0
   613
  if (!count)
slouken@0
   614
    {
slouken@782
   615
      if (song->buffered_count)
slouken@782
   616
          song->write(stream, song->common_buffer, channels * song->buffered_count);
slouken@782
   617
      song->buffer_pointer = song->common_buffer;
slouken@782
   618
      song->buffered_count = 0;
slouken@782
   619
      return;
slouken@0
   620
    }
slouken@0
   621
slouken@782
   622
  while ((count + song->buffered_count) >= song->buffer_size)
slouken@0
   623
    {
slouken@782
   624
      do_compute_data(song, song->buffer_size - song->buffered_count);
slouken@782
   625
      count -= song->buffer_size - song->buffered_count;
slouken@782
   626
      song->write(stream, song->common_buffer, channels * song->buffer_size);
slouken@782
   627
      song->buffer_pointer = song->common_buffer;
slouken@782
   628
      song->buffered_count = 0;
slouken@0
   629
    }
slouken@0
   630
  if (count>0)
slouken@0
   631
    {
slouken@782
   632
      do_compute_data(song, count);
slouken@782
   633
      song->buffered_count += count;
slouken@782
   634
      song->buffer_pointer += (song->encoding & PE_MONO) ? count : count*2;
slouken@0
   635
    }
slouken@0
   636
}
slouken@0
   637
slouken@782
   638
void Timidity_Start(MidiSong *song)
slouken@0
   639
{
slouken@782
   640
  song->playing = 1;
slouken@782
   641
  adjust_amplification(song);
slouken@782
   642
  skip_to(song, 0);
slouken@782
   643
}
slouken@782
   644
slouken@782
   645
void Timidity_Seek(MidiSong *song, Uint32 ms)
slouken@782
   646
{
slouken@782
   647
    skip_to(song, (ms * song->rate) / 1000);
slouken@782
   648
}
slouken@782
   649
slouken@782
   650
Uint32 Timidity_GetSongLength(MidiSong *song)
slouken@782
   651
{
slouken@782
   652
  MidiEvent *last_event = &song->events[song->groomed_event_count - 1];
slouken@782
   653
  /* We want last_event->time * 1000 / song->rate */
slouken@782
   654
  Uint32 retvalue = (last_event->time / song->rate) * 1000;
slouken@782
   655
  retvalue       += (last_event->time % song->rate) * 1000 / song->rate;
slouken@782
   656
  return retvalue;
slouken@782
   657
}
slouken@782
   658
slouken@782
   659
int Timidity_PlaySome(MidiSong *song, void *stream, Sint32 len)
slouken@782
   660
{
slouken@782
   661
  Sint32 start_sample, end_sample, samples;
slouken@782
   662
  int bytes_per_sample;
slouken@782
   663
slouken@782
   664
  if (!song->playing)
slouken@782
   665
    return 0;
slouken@0
   666
  
slouken@811
   667
  bytes_per_sample = 1;
slouken@811
   668
  bytes_per_sample *= ((song->encoding & PE_32BIT) ? 4 : ((song->encoding & PE_16BIT) ? 2 : 1));
slouken@811
   669
  bytes_per_sample *= ((song->encoding & PE_MONO) ? 1 : 2);
slouken@782
   670
  samples = len / bytes_per_sample;
slouken@782
   671
  
slouken@782
   672
  start_sample = song->current_sample;
slouken@782
   673
  end_sample = song->current_sample+samples;
slouken@782
   674
  while ( song->current_sample < end_sample ) {
slouken@0
   675
    /* Handle all events that should happen at this time */
slouken@782
   676
    while (song->current_event->time <= song->current_sample) {
slouken@782
   677
      switch(song->current_event->type) {
slouken@0
   678
slouken@0
   679
        /* Effects affecting a single note */
slouken@0
   680
slouken@0
   681
        case ME_NOTEON:
slouken@782
   682
          if (!(song->current_event->b)) /* Velocity 0? */
slouken@782
   683
            note_off(song);
slouken@0
   684
          else
slouken@782
   685
            note_on(song);
slouken@0
   686
          break;
slouken@0
   687
  
slouken@0
   688
        case ME_NOTEOFF:
slouken@782
   689
          note_off(song);
slouken@0
   690
          break;
slouken@0
   691
  
slouken@0
   692
        case ME_KEYPRESSURE:
slouken@782
   693
          adjust_pressure(song);
slouken@0
   694
          break;
slouken@0
   695
  
slouken@0
   696
          /* Effects affecting a single channel */
slouken@0
   697
  
slouken@0
   698
        case ME_PITCH_SENS:
slouken@782
   699
          song->channel[song->current_event->channel].pitchsens =
slouken@782
   700
	    song->current_event->a;
slouken@782
   701
          song->channel[song->current_event->channel].pitchfactor = 0;
slouken@0
   702
          break;
slouken@0
   703
          
slouken@0
   704
        case ME_PITCHWHEEL:
slouken@782
   705
          song->channel[song->current_event->channel].pitchbend =
slouken@782
   706
            song->current_event->a + song->current_event->b * 128;
slouken@782
   707
          song->channel[song->current_event->channel].pitchfactor = 0;
slouken@0
   708
          /* Adjust pitch for notes already playing */
slouken@782
   709
          adjust_pitchbend(song);
slouken@0
   710
          break;
slouken@0
   711
          
slouken@0
   712
        case ME_MAINVOLUME:
slouken@782
   713
          song->channel[song->current_event->channel].volume =
slouken@782
   714
	    song->current_event->a;
slouken@782
   715
          adjust_volume(song);
slouken@0
   716
          break;
slouken@782
   717
          
slouken@0
   718
        case ME_PAN:
slouken@782
   719
          song->channel[song->current_event->channel].panning =
slouken@782
   720
	    song->current_event->a;
slouken@0
   721
          break;
slouken@0
   722
          
slouken@0
   723
        case ME_EXPRESSION:
slouken@782
   724
          song->channel[song->current_event->channel].expression =
slouken@782
   725
	    song->current_event->a;
slouken@782
   726
          adjust_volume(song);
slouken@0
   727
          break;
slouken@0
   728
  
slouken@0
   729
        case ME_PROGRAM:
slouken@782
   730
          if (ISDRUMCHANNEL(song, song->current_event->channel)) {
slouken@0
   731
            /* Change drum set */
slouken@782
   732
            song->channel[song->current_event->channel].bank =
slouken@782
   733
	      song->current_event->a;
slouken@0
   734
          }
slouken@0
   735
          else
slouken@782
   736
            song->channel[song->current_event->channel].program =
slouken@782
   737
	      song->current_event->a;
slouken@0
   738
          break;
slouken@0
   739
  
slouken@0
   740
        case ME_SUSTAIN:
slouken@782
   741
          song->channel[song->current_event->channel].sustain =
slouken@782
   742
	    song->current_event->a;
slouken@782
   743
          if (!song->current_event->a)
slouken@782
   744
            drop_sustain(song);
slouken@0
   745
          break;
slouken@0
   746
          
slouken@0
   747
        case ME_RESET_CONTROLLERS:
slouken@782
   748
          reset_controllers(song, song->current_event->channel);
slouken@0
   749
          break;
slouken@0
   750
  
slouken@0
   751
        case ME_ALL_NOTES_OFF:
slouken@782
   752
          all_notes_off(song);
slouken@0
   753
          break;
slouken@0
   754
          
slouken@0
   755
        case ME_ALL_SOUNDS_OFF:
slouken@782
   756
          all_sounds_off(song);
slouken@0
   757
          break;
slouken@782
   758
          
slouken@0
   759
        case ME_TONE_BANK:
slouken@782
   760
          song->channel[song->current_event->channel].bank =
slouken@782
   761
	    song->current_event->a;
slouken@0
   762
          break;
slouken@782
   763
  
slouken@0
   764
        case ME_EOT:
slouken@0
   765
          /* Give the last notes a couple of seconds to decay  */
slouken@782
   766
          SNDDBG(("Playing time: ~%d seconds\n",
slouken@782
   767
		  song->current_sample/song->rate+2));
slouken@782
   768
          SNDDBG(("Notes cut: %d\n", song->cut_notes));
slouken@782
   769
          SNDDBG(("Notes lost totally: %d\n", song->lost_notes));
slouken@782
   770
	  song->playing = 0;
slouken@782
   771
          return (song->current_sample - start_sample) * bytes_per_sample;
slouken@0
   772
        }
slouken@782
   773
      song->current_event++;
slouken@0
   774
    }
slouken@782
   775
    if (song->current_event->time > end_sample)
slouken@782
   776
      compute_data(song, stream, end_sample-song->current_sample);
slouken@0
   777
    else
slouken@782
   778
      compute_data(song, stream, song->current_event->time-song->current_sample);
slouken@0
   779
  }
slouken@782
   780
  return samples * bytes_per_sample;
slouken@0
   781
}
slouken@0
   782
slouken@782
   783
void Timidity_SetVolume(MidiSong *song, int volume)
slouken@0
   784
{
slouken@0
   785
  int i;
slouken@0
   786
  if (volume > MAX_AMPLIFICATION)
slouken@782
   787
    song->amplification = MAX_AMPLIFICATION;
slouken@0
   788
  else
slouken@0
   789
  if (volume < 0)
slouken@782
   790
    song->amplification = 0;
slouken@0
   791
  else
slouken@782
   792
    song->amplification = volume;
slouken@782
   793
  adjust_amplification(song);
slouken@782
   794
  for (i = 0; i < song->voices; i++)
slouken@782
   795
    if (song->voice[i].status != VOICE_FREE)
slouken@0
   796
      {
slouken@782
   797
        recompute_amp(song, i);
slouken@782
   798
        apply_envelope_to_amp(song, i);
slouken@0
   799
      }
slouken@0
   800
}