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

📄 gus_wave.c

📁 内核是系统的心脏
💻 C
📖 第 1 页 / 共 5 页
字号:
      if (!pcm_active)		/* 
				 * Voice not started yet 
				 */
	{
	  /* 
	   * The playback was not started yet (or there has been a pause).
	   * Start the voice (again) and ask for a rollover irq at the end of
	   * this_one block. If this_one one is last of the buffers, use just
	   * the normal loop with irq.
	   */

	  gus_voice_off ();	/* 
				 * It could already be running 
				 */
	  gus_rampoff ();
	  gus_voice_volume (1530 + (25 * gus_pcm_volume));
	  gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));

	  gus_write_addr (0x0a, dram_loc, is16bits);	/* 
							 * Starting position 
							 */
	  gus_write_addr (0x02, chn * pcm_banksize, is16bits);	/* 
								 * Loop start 
								 * location 
								 */

	  if (chn != 0)
	    gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk),
			    is16bits);	/* 
					 * Loop end location 
					 */
	}

      if (chn == 0)
	gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits);	/* 
										 * Loop 
										 * end 
										 * location 
										 */
      else
	mode[chn] |= 0x08;	/* 
				 * Enable loop 
				 */

      if (pcm_datasize[this_one] != pcm_bsize)
	{
	  /* 
	   * Incomplete block. Possibly the last one. 
	   */
	  if (chn == 0)
	    {
	      mode[chn] &= ~0x08;	/* 
					 * Disable loop 
					 */
	      mode[chn] |= 0x20;	/* 
					 * Enable loop IRQ 
					 */
	      voices[0].loop_irq_mode = LMODE_PCM_STOP;
	      ramp_mode[chn] = 0x03;	/* 
					 * No rollover bit 
					 */
	    }
	  else
	    {
	      gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits);	/* 
											 * Loop 
											 * end 
											 * location 
											 */
	      mode[chn] &= ~0x08;	/* 
					 * Disable loop 
					 */
	    }
	}

      RESTORE_INTR (flags);
    }

  for (chn = 0; chn < gus_sampling_channels; chn++)
    {
      DISABLE_INTR (flags);
      gus_select_voice (chn);
      gus_write8 (0x0d, ramp_mode[chn]);
      gus_voice_on (mode[chn]);
      RESTORE_INTR (flags);
    }

  pcm_active = 1;
}

static void
gus_transfer_output_block (int dev, unsigned long buf,
			   int total_count, int intrflag, int chn)
{
  /* 
   * This routine transfers one block of audio data to the DRAM. In mono mode
   * it's called just once. When in stereo mode, this_one routine is called
   * once for both channels.
   * 
   * The left/mono channel data is transferred to the beginning of dram and the
   * right data to the area pointed by gus_page_size.
   */

  int             this_one, count;
  unsigned long   flags;
  unsigned char   dma_command;
  unsigned long   address, hold_address;

  DISABLE_INTR (flags);

  count = total_count / gus_sampling_channels;

  if (chn == 0)
    {
      if (pcm_qlen >= pcm_nblk)
	printk ("GUS Warning: PCM buffers out of sync\n");

      this_one = pcm_current_block = pcm_tail;
      pcm_qlen++;
      pcm_tail = (pcm_tail + 1) % pcm_nblk;
      pcm_datasize[this_one] = count;
    }
  else
    this_one = pcm_current_block;

  gus_write8 (0x41, 0);		/* 
				 * Disable GF1 DMA 
				 */
  DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE);

  address = this_one * pcm_bsize;
  address += chn * pcm_banksize;

  if (sound_dsp_dmachan[dev] > 3)
    {
      hold_address = address;
      address = address >> 1;
      address &= 0x0001ffffL;
      address |= (hold_address & 0x000c0000L);
    }

  gus_write16 (0x42, (address >> 4) & 0xffff);	/* 
						 * DRAM DMA address 
						 */

  dma_command = 0x21;		/* 
				 * IRQ enable, DMA start 
				 */

  if (gus_sampling_bits != 8)
    dma_command |= 0x40;	/* 
				 * 16 bit _DATA_ 
				 */
  else
    dma_command |= 0x80;	/* 
				 * Invert MSB 
				 */

  if (sound_dsp_dmachan[dev] > 3)
    dma_command |= 0x04;	/* 
				 * 16 bit DMA channel 
				 */

  gus_write8 (0x41, dma_command);	/* 
					 * Kick on 
					 */

  if (chn == (gus_sampling_channels - 1))	/* 
						 * Last channel 
						 */
    {
      /* 
       * Last (right or mono) channel data 
       */
      active_device = GUS_DEV_PCM_DONE;
      if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize))
	{
	  play_next_pcm_block ();
	}
    }
  else				/* 
				 * * * Left channel data. The right channel
				 * is * * * transferred after DMA interrupt   */
    active_device = GUS_DEV_PCM_CONTINUE;

  RESTORE_INTR (flags);
}

static void
gus_sampling_output_block (int dev, unsigned long buf, int total_count,
			   int intrflag, int restart_dma)
{
  pcm_current_buf = buf;
  pcm_current_count = total_count;
  pcm_current_intrflag = intrflag;
  pcm_current_dev = dev;
  gus_transfer_output_block (dev, buf, total_count, intrflag, 0);
}

static void
gus_sampling_start_input (int dev, unsigned long buf, int count,
			  int intrflag, int restart_dma)
{
  unsigned long   flags;
  unsigned char   mode;

  DISABLE_INTR (flags);

  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);

  mode = 0xa0;			/* 
				 * DMA IRQ enable, invert MSB 
				 */

  if (sound_dsp_dmachan[dev] > 3)
    mode |= 0x04;		/* 
				 * 16 bit DMA channel 
				 */
  if (gus_sampling_channels > 1)
    mode |= 0x02;		/* 
				 * Stereo 
				 */
  mode |= 0x01;			/* 
				 * DMA enable 
				 */

  gus_write8 (0x49, mode);

  RESTORE_INTR (flags);
}

static int
gus_sampling_prepare_for_input (int dev, int bsize, int bcount)
{
  unsigned int    rate;

  rate = (9878400 / (gus_sampling_speed + 2)) / 16;

  gus_write8 (0x48, rate & 0xff);	/* 
					 * Set sampling frequency 
					 */

  if (gus_sampling_bits != 8)
    {
      printk ("GUS Error: 16 bit recording not supported\n");
      return RET_ERROR (EINVAL);
    }

  return 0;
}

static int
gus_sampling_prepare_for_output (int dev, int bsize, int bcount)
{
  int             i;

  long            mem_ptr, mem_size;

  mem_ptr = 0;
  mem_size = gus_mem_size / gus_sampling_channels;

  if (mem_size > (256 * 1024))
    mem_size = 256 * 1024;

  pcm_bsize = bsize / gus_sampling_channels;
  pcm_head = pcm_tail = pcm_qlen = 0;

  pcm_nblk = MAX_PCM_BUFFERS;
  if ((pcm_bsize * pcm_nblk) > mem_size)
    pcm_nblk = mem_size / pcm_bsize;

  for (i = 0; i < pcm_nblk; i++)
    pcm_datasize[i] = 0;

  pcm_banksize = pcm_nblk * pcm_bsize;

  if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024))
    pcm_nblk--;

  return 0;
}

static int
gus_has_output_drained (int dev)
{
  return !pcm_qlen;
}

static void
gus_copy_from_user (int dev, char *localbuf, int localoffs,
		    snd_rw_buf * userbuf, int useroffs, int len)
{
  if (gus_sampling_channels == 1)
    {
      COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len);
    }
  else if (gus_sampling_bits == 8)
    {
      int             in_left = useroffs;
      int             in_right = useroffs + 1;
      char           *out_left, *out_right;
      int             i;

      len /= 2;
      localoffs /= 2;
      out_left = &localbuf[localoffs];
      out_right = out_left + pcm_bsize;

      for (i = 0; i < len; i++)
	{
	  GET_BYTE_FROM_USER (*out_left++, userbuf, in_left);
	  in_left += 2;
	  GET_BYTE_FROM_USER (*out_right++, userbuf, in_right);
	  in_right += 2;
	}
    }
  else
    {
      int             in_left = useroffs;
      int             in_right = useroffs + 1;
      short          *out_left, *out_right;
      int             i;

      len /= 4;
      localoffs /= 4;

      out_left = (short *) &localbuf[localoffs];
      out_right = out_left + (pcm_bsize / 2);

      for (i = 0; i < len; i++)
	{
	  GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left);
	  in_left += 2;
	  GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right);
	  in_right += 2;
	}
    }
}

static struct audio_operations gus_sampling_operations =
{
  "Gravis UltraSound",
  gus_sampling_open,
  gus_sampling_close,
  gus_sampling_output_block,
  gus_sampling_start_input,
  gus_sampling_ioctl,
  gus_sampling_prepare_for_input,
  gus_sampling_prepare_for_output,
  gus_sampling_reset,
  gus_sampling_reset,
  gus_has_output_drained,
  gus_copy_from_user
};

#ifdef FUTURE_VERSION
static void
guswave_bender (int dev, int voice, int value)
{
  int             freq;
  unsigned long   flags;

  voices[voice].bender = value - 8192;
  freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range);
  voices[voice].current_freq = freq;

  DISABLE_INTR (flags);
  gus_select_voice (voice);
  gus_voice_freq (freq);
  RESTORE_INTR (flags);
}
#endif

static int
guswave_patchmgr (int dev, struct patmgr_info *rec)
{
  int             i, n;

  switch (rec->command)
    {
    case PM_GET_DEVTYPE:
      rec->parm1 = PMTYPE_WAVE;
      return 0;
      break;

    case PM_GET_NRPGM:
      rec->parm1 = MAX_PATCH;
      return 0;
      break;

    case PM_GET_PGMMAP:
      rec->parm1 = MAX_PATCH;

      for (i = 0; i < MAX_PATCH; i++)
	{
	  int             ptr = patch_table[i];

	  rec->data.data8[i] = 0;

	  while (ptr >= 0 && ptr < free_sample)
	    {
	      rec->data.data8[i]++;
	      ptr = samples[ptr].key;	/* 
					 * Follow link 
					 */
	    }
	}
      return 0;
      break;

    case PM_GET_PGM_PATCHES:
      {
	int             ptr = patch_table[rec->parm1];

	n = 0;

	while (ptr >= 0 && ptr < free_sample)
	  {
	    rec->data.data32[n++] = ptr;
	    ptr = samples[ptr].key;	/* 
					 * Follow link 
					 */
	  }
      }
      rec->parm1 = n;
      return 0;
      break;

    case PM_GET_PATCH:
      {
	int             ptr = rec->parm1;
	struct patch_info *pat;

	if (ptr < 0 || ptr >= free_sample)
	  return RET_ERROR (EINVAL);

	memcpy (rec->data.data8, (char *) &samples[ptr],
		sizeof (struct patch_info));

	pat = (struct patch_info *) rec->data.data8;

	pat->key = GUS_PATCH;	/* 
				 * Restore patch type 
				 */
	rec->parm1 = sample_ptrs[ptr];	/* 
					 * DRAM address 
					 */
	rec->parm2 = sizeof (struct patch_info);
      }
      return 0;
      break;

    case PM_SET_PATCH:
      {
	int             ptr = rec->parm1;
	struct patch_info *pat;

	if (ptr < 0 || ptr >= free_sample)
	  return RET_ERROR (EINVAL);

	pat = (struct patch_info *) rec->data.data8;

	if (pat->len > samples[ptr].len)	/* 
						 * Cannot expand sample 
						 */
	  return RET_ERROR (EINVAL);

	pat->key = samples[ptr].key;	/* 
					 * Ensure the link is correct 
					 */

	memcpy ((char *) &samples[ptr], rec->data.data8,
		sizeof (struct patch_info));

	pat->key = GUS_PATCH;
      }
      return 0;
      break;

    case PM_READ_PATCH:	/* 
				 * Returns a block of wave data from the DRAM 
				 */
      {
	int             sample = rec->parm1;
	int             n;
	long            offs = rec->parm2;
	int             l = rec->parm3;

	if (sample < 0 || sample >= free_sample)
	  return RET_ERROR (EINVAL);

	if (offs < 0 || offs >= samples[sample].len)
	  return RET_ERROR (EINVAL);	/* 
					 * Invalid offset 
					 */

	n = samples[sample].len - offs;		/* 
						 * Nr of bytes left 
						 */

	if (l > n)
	  l = n;

	if (l > sizeof (rec->data.data8))
	  l = sizeof (rec->data.data8);

	if (l <= 0)
	  return RET_ERROR (EINVAL);	/* 
					 * Was there a bug? 
					 */

	offs += sample_ptrs[sample];	/* 
					 * Begin offsess + offset to DRAM 
					 */

	for (n = 0; n < l; n++)
	  rec->data.data8[n] = gus_peek (offs++);
	rec->parm1 = n;		/* 
				 * Nr of bytes copied 
				 */
      }
      return 0;
      break;

    case PM_WRITE_PATCH:	/* 
				 * Writes a block of wave data to the DRAM 
				 */
      {
	int             sample = rec->parm1;
	int             n;
	long            offs = rec->parm2;
	int             l = rec->parm3;

	if (sample < 0 || sample >= free_sample)
	  return RET_ERROR (EINVAL);

	if (offs < 0 || offs >= samples[sample].len)
	  return RET_ERROR (EINVAL);	/* 
					 * Invalid offset 
					 */

	n = samples[sample].len - offs;		/* 
						 * Nr of bytes left 
						 */

	if (l > n)
	  l = n;

	if (l > sizeof (rec->data.data8))
	  l = sizeof (rec->data.data8);

	if (l <= 0)
	  return RET_ERROR (EINVAL);	/* 
					 * Was there a bug? 
					 */

	offs += sample_ptrs[sample];	/* 
					 * Begin offsess + offset to DRAM 
					 */

	for (n = 0; n < l; n++)
	  gus_poke (offs++, rec->data.data8[n]);
	rec->parm1 = n;		/* 
				 * Nr of bytes copied 
				 */
      }
      return 0;
      break;

    default:
      return RET_ERROR (EINVAL);
    }
}

static struct synth_operations guswave_operations =
{
  &gus_info,
#ifdef FUTURE_VERSION
  0,
#endif
  SYNTH_TYPE_SAMPLE,
  SAMPLE_TYPE_GUS,
  guswave_open,
  guswave_close,
  guswave_ioctl,
  guswave_kill_note,
  guswave_start_note,
  guswave_set_instr,
  guswave_reset,
  gus

⌨️ 快捷键说明

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