📄 gus_wave.c
字号:
for (voice = 0; voice < gus_audio_channels; voice++) { save_flags(flags); cli(); gus_select_voice(voice); gus_rampoff(); gus_voice_volume(1530 + (25 * gus_pcm_volume)); gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); restore_flags(flags); }}static void play_next_pcm_block(void){ unsigned long flags; int speed = gus_audio_speed; int this_one, is16bits, chn; unsigned long dram_loc; unsigned char mode[2], ramp_mode[2]; if (!pcm_qlen) return; this_one = pcm_head; for (chn = 0; chn < gus_audio_channels; chn++) { mode[chn] = 0x00; ramp_mode[chn] = 0x03; /* Ramping and rollover off */ if (chn == 0) { mode[chn] |= 0x20; /* Loop IRQ */ voices[chn].loop_irq_mode = LMODE_PCM; } if (gus_audio_bits != 8) { is16bits = 1; mode[chn] |= 0x04; /* 16 bit data */ } else is16bits = 0; dram_loc = this_one * pcm_bsize; dram_loc += chn * pcm_banksize; if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ { mode[chn] |= 0x08; /* Enable loop */ ramp_mode[chn] = 0x03; /* Disable rollover bit */ } else { if (chn == 0) ramp_mode[chn] = 0x04; /* Enable rollover bit */ } save_flags(flags); cli(); gus_select_voice(chn); gus_voice_freq(speed); if (gus_audio_channels == 1) gus_voice_balance(7); /* mono */ else if (chn == 0) gus_voice_balance(0); /* left */ else gus_voice_balance(15); /* right */ if (!pcm_active) /* Playback not already active */ { /* * 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(); gus_rampoff(); gus_voice_volume(1530 + (25 * gus_pcm_volume)); gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */ gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ if (chn != 0) gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, 0, is16bits); /* Loop end location */ } if (chn == 0) gus_write_addr(0x04, dram_loc + pcm_bsize - 1, 0, is16bits); /* Loop end location */ else mode[chn] |= 0x08; /* Enable looping */ restore_flags(flags); } for (chn = 0; chn < gus_audio_channels; chn++) { save_flags(flags); cli(); gus_select_voice(chn); gus_write8(0x0d, ramp_mode[chn]); if (iw_mode) gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */ gus_voice_on(mode[chn]); restore_flags(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; save_flags(flags); cli(); count = total_count / gus_audio_channels; if (chn == 0) { if (pcm_qlen >= pcm_nblk) printk(KERN_WARNING "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 (audio_devs[dev]->dmap_out->dma > 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_audio_bits != 8) dma_command |= 0x40; /* 16 bit _DATA_ */ else dma_command |= 0x80; /* Invert MSB */ if (audio_devs[dev]->dmap_out->dma > 3) dma_command |= 0x04; /* 16 bit DMA channel */ gus_write8(0x41, dma_command); /* Kick start */ if (chn == (gus_audio_channels - 1)) /* Last channel */ { /* * Last (right or mono) channel data */ dma_active = 1; /* DMA started. There is a unacknowledged buffer */ active_device = GUS_DEV_PCM_DONE; if (!pcm_active && (pcm_qlen > 1 || 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_flags(flags);}static void gus_uninterleave8(char *buf, int l){/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */ int i, p = 0, halfsize = l / 2; char *buf2 = buf + halfsize, *src = bounce_buf; memcpy(bounce_buf, buf, l); for (i = 0; i < halfsize; i++) { buf[i] = src[p++]; /* Left channel */ buf2[i] = src[p++]; /* Right channel */ }}static void gus_uninterleave16(short *buf, int l){/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */ int i, p = 0, halfsize = l / 2; short *buf2 = buf + halfsize, *src = (short *) bounce_buf; memcpy(bounce_buf, (char *) buf, l * 2); for (i = 0; i < halfsize; i++) { buf[i] = src[p++]; /* Left channel */ buf2[i] = src[p++]; /* Right channel */ }}static void gus_audio_output_block(int dev, unsigned long buf, int total_count, int intrflag){ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT; pcm_current_buf = buf; pcm_current_count = total_count; pcm_current_intrflag = intrflag; pcm_current_dev = dev; if (gus_audio_channels == 2) { char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys); if (gus_audio_bits == 8) gus_uninterleave8(b, total_count); else gus_uninterleave16((short *) b, total_count / 2); } gus_transfer_output_block(dev, buf, total_count, intrflag, 0);}static void gus_audio_start_input(int dev, unsigned long buf, int count, int intrflag){ unsigned long flags; unsigned char mode; save_flags(flags); cli(); DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ); mode = 0xa0; /* DMA IRQ enabled, invert MSB */ if (audio_devs[dev]->dmap_in->dma > 3) mode |= 0x04; /* 16 bit DMA channel */ if (gus_audio_channels > 1) mode |= 0x02; /* Stereo */ mode |= 0x01; /* DMA enable */ gus_write8(0x49, mode); restore_flags(flags);}static int gus_audio_prepare_for_input(int dev, int bsize, int bcount){ unsigned int rate; gus_audio_bsize = bsize; audio_devs[dev]->dmap_in->flags |= DMA_NODMA; rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; gus_write8(0x48, rate & 0xff); /* Set sampling rate */ if (gus_audio_bits != 8) {/* printk("GUS Error: 16 bit recording not supported\n");*/ return -EINVAL; } return 0;}static int gus_audio_prepare_for_output(int dev, int bsize, int bcount){ int i; long mem_ptr, mem_size; audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT; mem_ptr = 0; mem_size = gus_mem_size / gus_audio_channels; if (mem_size > (256 * 1024)) mem_size = 256 * 1024; pcm_bsize = bsize / gus_audio_channels; pcm_head = pcm_tail = pcm_qlen = 0; pcm_nblk = 2; /* 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_audio_bits != 8 && pcm_banksize == (256 * 1024)) pcm_nblk--; gus_write8(0x41, 0); /* Disable GF1 DMA */ return 0;}static int gus_local_qlen(int dev){ return pcm_qlen;}static struct audio_driver gus_audio_driver ={ owner: THIS_MODULE, open: gus_audio_open, close: gus_audio_close, output_block: gus_audio_output_block, start_input: gus_audio_start_input, ioctl: gus_audio_ioctl, prepare_for_input: gus_audio_prepare_for_input, prepare_for_output: gus_audio_prepare_for_output, halt_io: gus_audio_reset, local_qlen: gus_local_qlen,};static void guswave_setup_voice(int dev, int voice, int chn){ struct channel_info *info = &synth_devs[dev]->chn_info[chn]; guswave_set_instr(dev, voice, info->pgm_num); voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; voices[voice].bender = 0; voices[voice].bender_range = info->bender_range; if (chn == 9) voices[voice].fixed_pitch = 1;}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 - 8192, voices[voice].bender_range, 0); voices[voice].current_freq = freq; save_flags(flags); cli(); gus_select_voice(voice); gus_voice_freq(freq); restore_flags(flags);}static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc){ int i, p, best = -1, best_time = 0x7fffffff; p = alloc->ptr; /* * First look for a completely stopped voice */ for (i = 0; i < alloc->max_voice; i++) { if (alloc->map[p] == 0) { alloc->ptr = p; return p; } if (alloc->alloc_times[p] < best_time) { best = p; best_time = alloc->alloc_times[p]; } p = (p + 1) % alloc->max_voice; } /* * Then look for a releasing voice */ for (i = 0; i < alloc->max_voice; i++) { if (alloc->map[p] == 0xffff) { alloc->ptr = p; return p; } p = (p + 1) % alloc->max_voice; } if (best >= 0) p = best; alloc->ptr = p; return p;}static struct synth_operations guswave_operations ={ owner: THIS_MODULE, id: "GUS", info: &gus_info, midi_dev: 0, synth_type: SYNTH_TYPE_SAMPLE, synth_subtype: SAMPLE_TYPE_GUS, open: guswave_open, close: guswave_close, ioctl: guswave_ioctl, kill_note: guswave_kill_note, start_note: guswave_start_note, set_instr: guswave_set_instr, reset: guswave_reset, hw_control: guswave_hw_control, load_patch: guswave_load_patch, aftertouch: guswave_aftertouch, controller: guswave_controller, panning: guswave_panning, volume_method: guswave_volume_method, bender: guswave_bender, alloc_voice: guswave_alloc, setup_voice: guswave_setup_voice};static void set_input_volumes(void){ unsigned long flags; unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ if (have_gus_max) /* Don't disturb GUS MAX */ return; save_flags(flags); cli(); /* * Enable channels having vol > 10% * Note! bit 0x01 means the line in DISABLED while 0x04 means * the mic in ENABLED. */ if (gus_line_vol > 10) mask &= ~0x01; if (gus_mic_vol > 10) mask |= 0x04; if (recording_active) { /* * Disable channel, if not selected for recording */ if (!(gus_recmask & SOUND_MASK_LINE)) mask |= 0x01; if (!(gus_recmask & SOUND_MASK_MIC)) mask &= ~0x04; } mix_image &= ~0x07; mix_image |= mask & 0x07; outb((mix_image), u_Mixer); restore_flags(flags);}#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ SOUND_MASK_SYNTH|SOUND_MASK_PCM)int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg){ int vol, val; if (((cmd >> 8) & 0xff) != 'M') return -EINVAL; if (!access_ok(VERIFY_WRITE, (int *)arg, sizeof(int))) return -EFAULT; if (_SIOC_DIR(cmd) & _SIOC_WRITE) { if (__get_user(val, (int *) arg)) return -EFAULT; switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: gus_recmask = val & MIX_DEVS; if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) gus_recmask = SOUND_MASK_MIC; /* Note! Input volumes are updated during next open for recording */ val = gus_recmask; break; case SOUND_MIXER_MIC: vol = val & 0xff; if (vol < 0) vol = 0; if (vol > 100) vol = 100; gus_mic_vol = vol; set_input_volumes(); val = vol | (vol << 8); break; case SOUND_MIXER_LINE: vol = val & 0xff; if (vol < 0) vol = 0; if (vol > 100) vol = 100; gus_line_vol = vol; set_input_volumes(); val = vol | (vol << 8); break; case SOUND_MIXER_PCM: gus_pcm_volume = val & 0xff; if (gus_pcm_volume < 0) gus_pcm_volume = 0; if (gus_pcm_volume > 100) gus_pcm_volume = 100; gus_audio_update_volume(); val = gus_pcm_volume | (gus_pcm_volume << 8); break; case SOUND_MIXER_SYNTH: gus_wave_volume = val & 0xff; if (gus_wave_volume < 0) gus_wave_volume = 0; if (gus_wave_volume > 100) gus_wave_volume = 100; if (active_device == GUS_DEV_WAVE) { int voice; for (voice = 0; voice < nr_voices; voice++) dynamic_volume_change(voice); /* Apply the new vol */ } val = gus_wave_volume | (gus_wave_volume << 8); break; default: return -EINVAL; } } else { switch (cmd & 0xff) { /* * Return parameters */ case SOUND_MIXER_RECSRC: val = gus_recmask; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -