⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 playmidi.c

📁 play midi good
💻 C
📖 第 1 页 / 共 5 页
字号:
void recompute_freq(int v)
{
  int ch=voice[v].channel,note=voice[v].note;
  int
    sign=(voice[v].sample_increment < 0), /* for bidirectional loops */
    pb=channel[ch].pitchbend;
  double a;
  int32 tuning = 0;


  if(!voice[v].sample->sample_rate)
      return;

  if(!opt_modulation_wheel)
      voice[v].modulation_wheel = 0;
  if(!opt_portamento)
      voice[v].porta_control_ratio = 0;

  voice[v].vibrato_control_ratio = voice[v].orig_vibrato_control_ratio;
  if(voice[v].vibrato_control_ratio || voice[v].modulation_wheel > 0)
  {
      /* This instrument has vibrato. Invalidate any precomputed
         sample_increments. */
      int i;
      if(voice[v].modulation_wheel > 0)
      {
	  voice[v].vibrato_control_ratio =
	      (int32)(play_mode->rate * MODULATION_WHEEL_RATE
		      / (2.0 * VIBRATO_SAMPLE_INCREMENTS));
	  voice[v].vibrato_delay = 0;
      }
      for(i = 0; i < VIBRATO_SAMPLE_INCREMENTS; i++)
	  voice[v].vibrato_sample_increment[i] = 0;
      voice[v].cache = NULL;
  }

  /* fine: [0..128] => [-256..256]
   * 1 coarse = 256 fine (= 1 note)
   * 1 fine = 2^5 tuning
   */
  tuning = (((int32)channel[ch].rpnmap[RPN_ADDR_0001] - 0x40)
	    + 64 * ((int32)channel[ch].rpnmap[RPN_ADDR_0002] - 0x40)) << 7;

  if(!voice[v].porta_control_ratio)
  {
      if(tuning == 0 && pb == 0x2000) {
	voice[v].frequency = voice[v].orig_frequency;
      }
      else
      {
	  pb -= 0x2000;
	  if(!(channel[ch].pitchfactor))
	  {
	      /* Damn. Somebody bent the pitch. */
	      int32 i = (int32)pb * channel[ch].rpnmap[RPN_ADDR_0000] + tuning;
	      if(i >= 0)
		  channel[ch].pitchfactor =
		      bend_fine[(i>>5) & 0xFF] * bend_coarse[(i>>13) & 0x7F];
	      else
	      {
		  i = -i;
		  channel[ch].pitchfactor = 1.0 /
		      (bend_fine[(i>>5) & 0xFF] * bend_coarse[(i>>13) & 0x7F]);
	      }
	  }
	  voice[v].frequency =
	      (int32)((double)voice[v].orig_frequency
		      * channel[ch].pitchfactor);
	  if(voice[v].frequency != voice[v].orig_frequency)
	      voice[v].cache = NULL;
      }
  }
  else /* Portament */
  {
      int32 i;
      FLOAT_T pf;

      pb -= 0x2000;
      i = pb * channel[ch].rpnmap[RPN_ADDR_0000]
	  + (voice[v].porta_pb << 5) + tuning;
      if(i >= 0)
	  pf = bend_fine[(i>>5) & 0xFF] * bend_coarse[(i>>13) & 0x7F];
      else
      {
	  i = -i;
	  pf = 1.0 / (bend_fine[(i>>5) & 0xFF] * bend_coarse[(i>>13) & 0x7F]);
      }
      voice[v].frequency = (int32)((double)(voice[v].orig_frequency) * pf);
      voice[v].cache = NULL;
  }


  a = TIM_FSCALE(((double)voice[v].sample->sample_rate * voice[v].frequency) /
		 ((double)voice[v].sample->root_freq * play_mode->rate),
		 FRACTION_BITS) + 0.5;

  if(sign)
      a = -a; /* need to preserve the loop direction */

  voice[v].sample_increment = (int32)a;
#ifdef ABORT_AT_FATAL
  if (voice[v].sample_increment == 0) {
      fprintf(stderr, "Invalid sample increment a=%e %ld %ld %ld %ld%s\n",
	      a, (long)voice[v].sample->sample_rate, (long)voice[v].frequency,
	      (long)voice[v].sample->root_freq, (long)play_mode->rate,
	      voice[v].cache ? " (Cached)" : "");
      abort();
  }
#endif /* ABORT_AT_FATAL */
}

static void recompute_amp(int v)
{
    FLOAT_T tempamp, smsvol;
 /* original
	tempamp = ((FLOAT_T)master_volume *
		   velocity_table[voice[v].velocity] *
		   voice[v].sample->volume *
		   channel[voice[v].channel].volume *
		   channel[voice[v].channel].expression);
*/    /* 21 bits */

      /*  Stan Swanson, 045.31
	  master_volume and sample->volume are in amplitude units,
	   channel volume and expression undetermined 
             (if in veloc units, should use vol_table also)
	   Hence if tempamp is a product, should use vol_table[veloc] */


        smsvol = 500.0*vol_table[velocity_table[voice[v].velocity]];

	tempamp = ((FLOAT_T)master_volume *
		   smsvol *
		   voice[v].sample->volume *
		   channel[voice[v].channel].volume *
		   channel[voice[v].channel].expression); /* 21 bits */

/* sms debug 045.31  */
{ int VEL,TV,VOC,NOTE; double volume;
  NOTE = voice[v].note;
  VEL = voice[v].velocity;
  TV = velocity_table[VEL];  
  volume = vol_table[TV]; 
  ctl->cmsg(CMSG_INFO, VERB_DEBUG,
            "start_note %4d  vel %4d temp vel %4d vol_table %g \n", NOTE,VEL,TV,volume);
}
/* sms debug 045.31*/

	/* Level of Drum */
	if(ISDRUMCHANNEL(voice[v].channel)
		&& channel[voice[v].channel].drums[voice[v].note] != NULL
		&& channel[voice[v].channel].drums[voice[v].note]->drum_level != 1) {
		tempamp *= channel[voice[v].channel].drums[voice[v].note]->drum_level;
	}

    if(!(play_mode->encoding & PE_MONO))
    {
	if(voice[v].panning > 60 && voice[v].panning < 68)
	{
	    voice[v].panned = PANNED_CENTER;
	    voice[v].left_amp = TIM_FSCALENEG(tempamp, 21);
	}
	else if (voice[v].panning < 5)
	{
	    voice[v].panned = PANNED_LEFT;
	    voice[v].left_amp = TIM_FSCALENEG(tempamp, 20);
	}
	else if(voice[v].panning > 123)
	{
	    voice[v].panned = PANNED_RIGHT;
	    /* left_amp will be used */
	    voice[v].left_amp =  TIM_FSCALENEG(tempamp, 20);
	}
	else
	{
	    voice[v].panned = PANNED_MYSTERY;
	    voice[v].left_amp = TIM_FSCALENEG(tempamp, 27);
	    voice[v].right_amp = voice[v].left_amp * voice[v].panning;
	    voice[v].left_amp *= (FLOAT_T)(127 - voice[v].panning);
	}
    }
    else
    {
	voice[v].panned = PANNED_CENTER;
	voice[v].left_amp = TIM_FSCALENEG(tempamp, 21);
    }
}

Instrument *play_midi_load_instrument(int dr, int bk, int prog)
{
    ToneBank **bank = ((dr) ? drumset : tonebank);
    Instrument *ip;
    int load_success;

    if(bank[bk] == NULL)
	bk = 0;

    load_success = 0;
    if(opt_realtime_playing != 2)
    {
	if((ip = bank[bk]->tone[prog].instrument) == MAGIC_LOAD_INSTRUMENT)
	{
	    ip = bank[bk]->tone[prog].instrument =
		load_instrument(dr, bk, prog);
	    if(ip != NULL)
		load_success = 1;
	}
	if(ip == NULL && bk != 0)
	{
	    /* Instrument is not found.
	       Retry to load the instrument from bank 0 */

	    if((ip = bank[0]->tone[prog].instrument) == MAGIC_LOAD_INSTRUMENT)
		ip = bank[0]->tone[prog].instrument =
		    load_instrument(dr, 0, prog);
	    if(ip != NULL)
	    {
		bank[bk]->tone[prog].instrument = ip;
		load_success = 1;
	    }
	}
    }
    else
    {
	if((ip = bank[bk]->tone[prog].instrument) == NULL)
	{
	    ip = bank[bk]->tone[prog].instrument =
		load_instrument(dr, bk, prog);
	    if(ip != NULL)
		load_success = 1;
	}
	if(ip == NULL && bk != 0)
	{
	    /* Instrument is not found.
	       Retry to load the instrument from bank 0 */
	    if((ip = bank[0]->tone[prog].instrument) == NULL)
		ip = bank[0]->tone[prog].instrument =
		    load_instrument(dr, 0, prog);
	    if(ip != NULL)
	    {
		bank[bk]->tone[prog].instrument = ip;
		load_success = 1;
	    }
	}
    }

    if(load_success)
	aq_add(NULL, 0); /* Update software buffer */

    if(ip == MAGIC_ERROR_INSTRUMENT)
	return NULL;
    if(ip == NULL)
	bank[bk]->tone[prog].instrument = MAGIC_ERROR_INSTRUMENT;

    return ip;
}

#if 0
/* reduce_voice_CPU() may not have any speed advantage over reduce_voice().
 * So this function is not used, now.
 */

/* The goal of this routine is to free as much CPU as possible without
   loosing too much sound quality.  We would like to know how long a note
   has been playing, but since we usually can't calculate this, we guess at
   the value instead.  A bad guess is better than nothing.  Notes which
   have been playing a short amount of time are killed first.  This causes
   decays and notes to be cut earlier, saving more CPU time.  It also causes
   notes which are closer to ending not to be cut as often, so it cuts
   a different note instead and saves more CPU in the long run.  ON voices
   are treated a little differently, since sound quality is more important
   than saving CPU at this point.  Duration guesses for loop regions are very
   crude, but are still better than nothing, they DO help.  Non-looping ON
   notes are cut before looping ON notes.  Since a looping ON note is more
   likely to have been playing for a long time, we want to keep it because it
   sounds better to keep long notes.
*/
static int reduce_voice_CPU(void)
{
    int32 lv, v, vr;
    int i, j, lowest=-0x7FFFFFFF;
    int32 duration;

    i = upper_voices;
    lv = 0x7FFFFFFF;
    
    /* Look for the decaying note with the longest remaining decay time */
    /* Protect drum decays.  They do not take as much CPU (?) and truncating
       them early sounds bad, especially on snares and cymbals */
    for(j = 0; j < i; j++)
    {
	if(voice[j].status & VOICE_FREE || voice[j].cache != NULL)
	    continue;
	/* skip notes that don't need resampling (most drums) */
	if (voice[j].sample->note_to_use)
	    continue;
	if(voice[j].status & ~(VOICE_ON | VOICE_DIE | VOICE_SUSTAINED))
	{
	    /* Choose note with longest decay time remaining */
	    /* This frees more CPU than choosing lowest volume */
	    if (!voice[j].envelope_increment) duration = 0;
	    else duration =
	    	(voice[j].envelope_target - voice[j].envelope_volume) /
	    	voice[j].envelope_increment;
	    v = -duration;
	    if(v < lv)
	    {
		lv = v;
		lowest = j;
	    }
	}
    }
    if(lowest != -0x7FFFFFFF)
    {
	/* This can still cause a click, but if we had a free voice to
	   spare for ramping down this note, we wouldn't need to kill it
	   in the first place... Still, this needs to be fixed. Perhaps
	   we could use a reserve of voices to play dying notes only. */

	cut_notes++;
	return lowest;
    }

    /* try to remove VOICE_DIE before VOICE_ON */
    lv = 0x7FFFFFFF;
    lowest = -1;
    for(j = 0; j < i; j++)
    {
      if(voice[j].status & VOICE_FREE || voice[j].cache != NULL)
	    continue;
      if(voice[j].status & ~(VOICE_ON | VOICE_SUSTAINED))
      {
	/* continue protecting non-resample decays */
	if (voice[j].status & ~(VOICE_DIE) && voice[j].sample->note_to_use)
		continue;

	/* choose note which has been on the shortest amount of time */
	/* this is a VERY crude estimate... */
	if (voice[j].sample->modes & MODES_LOOPING)
	    duration = voice[j].sample_offset - voice[j].sample->loop_start;
	else
	    duration = voice[j].sample_offset;
	if (voice[j].sample_increment > 0)
	    duration /= voice[j].sample_increment;
	v = duration;
	if(v < lv)
	{
	    lv = v;
	    lowest = j;
	}
      }
    }
    if(lowest != -1)
    {
	cut_notes++;
	return lowest;
    }

    /* try to remove VOICE_SUSTAINED before VOICE_ON */
    lv = 0x7FFFFFFF;
    lowest = -0x7FFFFFFF;
    for(j = 0; j < i; j++)
    {
      if(voice[j].status & VOICE_FREE || voice[j].cache != NULL)
	    continue;
      if(voice[j].status & VOICE_SUSTAINED)
      {
	/* choose note which has been on the shortest amount of time */
	/* this is a VERY crude estimate... */
	if (voice[j].sample->modes & MODES_LOOPING)
	    duration = voice[j].sample_offset - voice[j].sample->loop_start;
	else
	    duration = voice[j].sample_offset;
	if (voice[j].sample_increment > 0)
	    duration /= voice[j].sample_increment;
	v = duration;
	if(v < lv)
	{
	    lv = v;
	    lowest = j;
	}
      }
    }
    if(lowest != -0x7FFFFFFF)
    {
	cut_notes++;
	return lowest;
    }

    /* try to remove chorus before VOICE_ON */
    lv = 0x7FFFFFFF;
    lowest = -0x7FFFFFFF;
    for(j = 0; j < i; j++)
    {
      if(voice[j].status & VOICE_FREE || voice[j].cache != NULL)
	    continue;
      if(voice[j].chorus_link < j)
      {
	/* score notes based on both volume AND duration */
	/* this scoring function needs some more tweaking... */
	if (voice[j].sample->modes & MODES_LOOPING)
	    duration = voice[j].sample_offset - voice[j].sample->loop_start;
	else
	    duration = voice[j].sample_offset;
	if (voice[j].sample_increment > 0)
	    duration /= voice[j].sample_increment;
	v = voice[j].left_mix * duration;
	vr = voice[j].right_mix * duration;
	if(voice[j].panned == PANNED_MYSTERY && vr > v)
	    v = vr;
	if(v < lv)
	{
	    lv = v;
	    lowest = j;
	}
      }
    }
    if(lowest != -0x7FFFFFFF)
    {
	cut_notes++;

	/* hack - double volume of chorus partner, fix pan */
	j = voice[lowest].chorus_link;
	voice[j].velocity <<= 1;
    	voice[j].panning = channel[voice[lowest].channel].panning;
    	recompute_amp(j);
    	apply_envelope_to_amp(j);

	return lowest;
    }

    lost_notes++;

    /* try to remove non-looping voices first */
    lv = 0x7FFFFFFF;
    lowest = -0x7FFFFFFF;
    for(j = 0; j < i; j++)
    {
      if(voice[j].status & VOICE_FREE || voice[j].cache != NULL)
	    continue;
      if(voice[j].sample->modes & ~MODES_LOOPING)
      {
	/* score notes based on both volume AND duration */
	/* this scoring function needs some more tweaking... */
	duration = voice[j].sample_offset;
	if (voice[j].sample_increment > 0)
	    duration /= voice[j].sample_increment;
	v = voice[j].left_mix * duration;
	vr = voice[j].right_mix * duration;
	if(voice[j].panned == PANNED_MYSTERY && vr > v)
	    v = vr;
	if(v < lv)
	{
	    lv = v;
	    lowest = j;
	}
      }
    }
    if(lowest != -0x7FFFFFFF)
    {
	return lowest;
    }

    lv = 0x7FFFFFFF;
    lowest = 0;
    for(j = 0; j < i; j++)
    {
	if(voice[j].status & VOICE_FREE || voice[j].cache != NULL)
	    continue;
	if (voice[j].sample->modes & ~MODES_LOOPING) continue;

	/* score notes based on both volume AND duration */
	/* this scoring function needs some more tweaking... */
	duration = voice[j].sample_offset - voice[j].sample->loop_start;
	if (voice[j].sample_increment > 0)
	    duration /= voice[j].sample_increment;
	v = voice[j].left_mix * duration;
	vr = voice[j].right_mix * duration;
	if(voice[j].panned == PANNED_MYSTERY && vr > v)
	    v = vr;
	if(v < lv)
	{
	    lv = v;
	    lowest = j;
	}
    }

    return lowest;
}
#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -