📄 gus_wave.c
字号:
/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/ return -EINVAL; /* Patch not defined */ } if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ {/* printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/ return -EINVAL; } sample_map[voice] = sample_no; patch_map[voice] = instr_no; return 0;}static int guswave_kill_note(int dev, int voice, int note, int velocity){ unsigned long flags; save_flags(flags); cli(); /* voice_alloc->map[voice] = 0xffff; */ if (voices[voice].volume_irq_mode == VMODE_START_NOTE) { voices[voice].kill_pending = 1; restore_flags(flags); } else { restore_flags(flags); gus_voice_fade(voice); } return 0;}static void guswave_aftertouch(int dev, int voice, int pressure){}static void guswave_panning(int dev, int voice, int value){ if (voice >= 0 || voice < 32) voices[voice].panning = value;}static void guswave_volume_method(int dev, int mode){ if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) volume_method = mode;}static void compute_volume(int voice, int volume){ if (volume < 128) voices[voice].midi_volume = volume; switch (volume_method) { case VOL_METHOD_ADAGIO: voices[voice].initial_volume = gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol, voices[voice].expression_vol, voices[voice].patch_vol); break; case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol); break; default: voices[voice].initial_volume = volume_base + (voices[voice].midi_volume * volume_scale); } if (voices[voice].initial_volume > 4030) voices[voice].initial_volume = 4030;}static void compute_and_set_volume(int voice, int volume, int ramp_time){ int curr, target, rate; unsigned long flags; compute_volume(voice, volume); voices[voice].current_volume = voices[voice].initial_volume; save_flags(flags); cli(); /* * CAUTION! Interrupts disabled. Enable them before returning */ gus_select_voice(voice); curr = gus_read16(0x09) >> 4; target = voices[voice].initial_volume; if (ramp_time == INSTANT_RAMP) { gus_rampoff(); gus_voice_volume(target); restore_flags(flags); return; } if (ramp_time == FAST_RAMP) rate = 63; else rate = 16; gus_ramp_rate(0, rate); if ((target - curr) / 64 == 0) /* Close enough to target. */ { gus_rampoff(); gus_voice_volume(target); restore_flags(flags); return; } if (target > curr) { if (target > (4095 - 65)) target = 4095 - 65; gus_ramp_range(curr, target); gus_rampon(0x00); /* Ramp up, once, no IRQ */ } else { if (target < 65) target = 65; gus_ramp_range(target, curr); gus_rampon(0x40); /* Ramp down, once, no irq */ } restore_flags(flags);}static void dynamic_volume_change(int voice){ unsigned char status; unsigned long flags; save_flags(flags); cli(); gus_select_voice(voice); status = gus_read8(0x00); /* Get voice status */ restore_flags(flags); if (status & 0x03) return; /* Voice was not running */ if (!(voices[voice].mode & WAVE_ENVELOPES)) { compute_and_set_volume(voice, voices[voice].midi_volume, 1); return; } /* * Voice is running and has envelopes. */ save_flags(flags); cli(); gus_select_voice(voice); status = gus_read8(0x0d); /* Ramping status */ restore_flags(flags); if (status & 0x03) /* Sustain phase? */ { compute_and_set_volume(voice, voices[voice].midi_volume, 1); return; } if (voices[voice].env_phase < 0) return; compute_volume(voice, voices[voice].midi_volume);}static void guswave_controller(int dev, int voice, int ctrl_num, int value){ unsigned long flags; unsigned long freq; if (voice < 0 || voice > 31) return; switch (ctrl_num) { case CTRL_PITCH_BENDER: voices[voice].bender = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) { freq = compute_finetune(voices[voice].orig_freq, value, 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); } break; case CTRL_PITCH_BENDER_RANGE: voices[voice].bender_range = value; break; case CTL_EXPRESSION: value /= 128; case CTRL_EXPRESSION: if (volume_method == VOL_METHOD_ADAGIO) { voices[voice].expression_vol = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) dynamic_volume_change(voice); } break; case CTL_PAN: voices[voice].panning = (value * 2) - 128; break; case CTL_MAIN_VOLUME: value = (value * 100) / 16383; case CTRL_MAIN_VOLUME: voices[voice].main_vol = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) dynamic_volume_change(voice); break; default: break; }}static int guswave_start_note2(int dev, int voice, int note_num, int volume){ int sample, best_sample, best_delta, delta_freq; int is16bits, samplep, patch, pan; unsigned long note_freq, base_note, freq, flags; unsigned char mode = 0; if (voice < 0 || voice > 31) {/* printk("GUS: Invalid voice\n");*/ return -EINVAL; } if (note_num == 255) { if (voices[voice].mode & WAVE_ENVELOPES) { voices[voice].midi_volume = volume; dynamic_volume_change(voice); return 0; } compute_and_set_volume(voice, volume, 1); return 0; } if ((patch = patch_map[voice]) == -1) return -EINVAL; if ((samplep = patch_table[patch]) == NOT_SAMPLE) { return -EINVAL; } note_freq = note_to_freq(note_num); /* * Find a sample within a patch so that the note_freq is between low_note * and high_note. */ sample = -1; best_sample = samplep; best_delta = 1000000; while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) { delta_freq = note_freq - samples[samplep].base_note; if (delta_freq < 0) delta_freq = -delta_freq; if (delta_freq < best_delta) { best_sample = samplep; best_delta = delta_freq; } if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note) { sample = samplep; } else samplep = samples[samplep].key; /* Link to next sample */ } if (sample == -1) sample = best_sample; if (sample == -1) {/* printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/ return 0; /* Should play default patch ??? */ } is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; voices[voice].mode = samples[sample].mode; voices[voice].patch_vol = samples[sample].volume; if (iw_mode) gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */ if (voices[voice].mode & WAVE_ENVELOPES) { int i; for (i = 0; i < 6; i++) { voices[voice].env_rate[i] = samples[sample].env_rate[i]; voices[voice].env_offset[i] = samples[sample].env_offset[i]; } } sample_map[voice] = sample; if (voices[voice].fixed_pitch) /* Fixed pitch */ { freq = samples[sample].base_freq; } else { base_note = samples[sample].base_note / 100; note_freq /= 100; freq = samples[sample].base_freq * note_freq / base_note; } voices[voice].orig_freq = freq; /* * Since the pitch bender may have been set before playing the note, we * have to calculate the bending now. */ freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range, 0); voices[voice].current_freq = freq; pan = (samples[sample].panning + voices[voice].panning) / 32; pan += 7; if (pan < 0) pan = 0; if (pan > 15) pan = 15; if (samples[sample].mode & WAVE_16_BITS) { mode |= 0x04; /* 16 bits */ if ((sample_ptrs[sample] / GUS_BANK_SIZE) != ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE)) printk(KERN_ERR "GUS: Sample address error\n"); } /************************************************************************* * CAUTION! Interrupts disabled. Don't return before enabling *************************************************************************/ save_flags(flags); cli(); gus_select_voice(voice); gus_voice_off(); gus_rampoff(); restore_flags(flags); if (voices[voice].mode & WAVE_ENVELOPES) { compute_volume(voice, volume); init_envelope(voice); } else { compute_and_set_volume(voice, volume, 0); } save_flags(flags); cli(); gus_select_voice(voice); if (samples[sample].mode & WAVE_LOOP_BACK) gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len - voices[voice].offset_pending, 0, is16bits); /* start=end */ else gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */ if (samples[sample].mode & WAVE_LOOPING) { mode |= 0x08; if (samples[sample].mode & WAVE_BIDIR_LOOP) mode |= 0x10; if (samples[sample].mode & WAVE_LOOP_BACK) { gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end - voices[voice].offset_pending, (samples[sample].fractions >> 4) & 0x0f, is16bits); mode |= 0x40; } gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, samples[sample].fractions & 0x0f, is16bits); /* Loop start location */ gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ } else { mode |= 0x20; /* Loop IRQ at the end */ voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ voices[voice].loop_irq_parm = 1; gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */ gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ } gus_voice_freq(freq); gus_voice_balance(pan); gus_voice_on(mode); restore_flags(flags); return 0;}/* * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking * when the note playing on the voice is changed. It uses volume * ramping. */static int guswave_start_note(int dev, int voice, int note_num, int volume){ long int flags; int mode; int ret_val = 0; save_flags(flags); cli(); if (note_num == 255) { if (voices[voice].volume_irq_mode == VMODE_START_NOTE) { voices[voice].volume_pending = volume; } else { ret_val = guswave_start_note2(dev, voice, note_num, volume); } } else { gus_select_voice(voice); mode = gus_read8(0x00); if (mode & 0x20) gus_write8(0x00, mode & 0xdf); /* No interrupt! */ voices[voice].offset_pending = 0; voices[voice].kill_pending = 0; voices[voice].volume_irq_mode = 0; voices[voice].loop_irq_mode = 0; if (voices[voice].sample_pending >= 0) { restore_flags(flags); /* Run temporarily with interrupts enabled */ guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending); voices[voice].sample_pending = -1; save_flags(flags); cli(); gus_select_voice(voice); /* Reselect the voice (just to be sure) */ } if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065)) { ret_val = guswave_start_note2(dev, voice, note_num, volume); } else { voices[voice].dev_pending = dev; voices[voice].note_pending = note_num; voices[voice].volume_pending = volume; voices[voice].volume_irq_mode = VMODE_START_NOTE; gus_rampoff(); gus_ramp_range(2000, 4065); gus_ramp_rate(0, 63); /* Fastest possible rate */ gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */ } } restore_flags(flags); return ret_val;}static void guswave_reset(int dev){ int i; for (i = 0; i < 32; i++) { gus_voice_init(i); gus_voice_init2(i); }}static int guswave_open(int dev, int mode){ int err; if (gus_busy) return -EBUSY; voice_alloc->timestamp = 0; if (gus_no_wave_dma) { gus_no_dma = 1; } else { if ((err = DMAbuf_open_dma(gus_devnum)) < 0) { /* printk( "GUS: Loading samples without DMA\n"); */ gus_no_dma = 1; /* Upload samples using PIO */ } else gus_no_dma = 0; } init_waitqueue_head(&dram_sleeper); gus_busy = 1; active_device = GUS_DEV_WAVE; gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ gus_initialize(); gus_reset(); gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ return 0;}static void guswave_close(int dev){ gus_busy = 0; active_device = 0; gus_reset(); if (!gus_no_dma) DMAbuf_close_dma(gus_devnum);}static int guswave_load_patch(int dev, int format, const char *addr, int offs, int count, int pmgr_flag){ struct patch_info patch; int instr; long sizeof_patch; unsigned long blk_sz, blk_end, left, src_offs, target; sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ if (format != GUS_PATCH) {/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/ return -EINVAL; } if (count < sizeof_patch) {/* printk("GUS Error: Patch header too short\n");*/ return -EINVAL; } count -= sizeof_patch; if (free_sample >= MAX_SAMPLE) {/* printk("GUS: Sample table full\n");*/ return -ENOSPC; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -