📄 emu8000_pcm.c
字号:
aux = 0xff; else aux = (-rec->panning[ch]) & 0xff; temp = (temp << 8) | (pt << 16) | aux; EMU8000_PTRX_WRITE(hw, ch, temp); EMU8000_CPF_WRITE(hw, ch, pt << 16); /* start timer */ spin_lock_irqsave(&rec->timer_lock, flags); if (! rec->timer_running) { rec->timer.expires = jiffies + 1; add_timer(&rec->timer); rec->timer_running = 1; } spin_unlock_irqrestore(&rec->timer_lock, flags);}/* * stop the voice immediately */static void stop_voice(emu8k_pcm_t *rec, int ch){ unsigned long flags; emu8000_t *hw = rec->emu; EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); /* stop timer */ spin_lock_irqsave(&rec->timer_lock, flags); if (rec->timer_running) { del_timer(&rec->timer); rec->timer_running = 0; } spin_unlock_irqrestore(&rec->timer_lock, flags);}static int emu8k_pcm_trigger(snd_pcm_substream_t *subs, int cmd){ emu8k_pcm_t *rec = subs->runtime->private_data; int ch; switch (cmd) { case SNDRV_PCM_TRIGGER_START: for (ch = 0; ch < rec->voices; ch++) start_voice(rec, ch); rec->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: rec->running = 0; for (ch = 0; ch < rec->voices; ch++) stop_voice(rec, ch); break; default: return -EINVAL; } return 0;}/* * copy / silence ops *//* * this macro should be inserted in the copy/silence loops * to reduce the latency. without this, the system will hang up * during the whole loop. */#define CHECK_SCHEDULER() \do { \ cond_resched();\ if (signal_pending(current))\ return -EAGAIN;\} while (0)#ifdef USE_NONINTERLEAVE/* copy one channel block */static int emu8k_transfer_block(emu8000_t *emu, int offset, unsigned short *buf, int count){ EMU8000_SMALW_WRITE(emu, offset); while (count > 0) { unsigned short sval; CHECK_SCHEDULER(); get_user(sval, buf); EMU8000_SMLD_WRITE(emu, sval); buf++; count--; } return 0;}static int emu8k_pcm_copy(snd_pcm_substream_t *subs, int voice, snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count){ emu8k_pcm_t *rec = subs->runtime->private_data; emu8000_t *emu = rec->emu; snd_emu8000_write_wait(emu, 1); if (voice == -1) { unsigned short *buf = src; int i, err; count /= rec->voices; for (i = 0; i < rec->voices; i++) { err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count); if (err < 0) return err; buf += count; } return 0; } else { return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count); }}/* make a channel block silence */static int emu8k_silence_block(emu8000_t *emu, int offset, int count){ EMU8000_SMALW_WRITE(emu, offset); while (count > 0) { CHECK_SCHEDULER(); EMU8000_SMLD_WRITE(emu, 0); count--; } return 0;}static int emu8k_pcm_silence(snd_pcm_substream_t *subs, int voice, snd_pcm_uframes_t pos, snd_pcm_uframes_t count){ emu8k_pcm_t *rec = subs->runtime->private_data; emu8000_t *emu = rec->emu; snd_emu8000_write_wait(emu, 1); if (voice == -1 && rec->voices == 1) voice = 0; if (voice == -1) { int err; err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2); if (err < 0) return err; return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2); } else { return emu8k_silence_block(emu, pos + rec->loop_start[voice], count); }}#else /* interleave *//* * copy the interleaved data can be done easily by using * DMA "left" and "right" channels on emu8k engine. */static int emu8k_pcm_copy(snd_pcm_substream_t *subs, int voice, snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count){ emu8k_pcm_t *rec = subs->runtime->private_data; emu8000_t *emu = rec->emu; unsigned short __user *buf = src; snd_emu8000_write_wait(emu, 1); EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); if (rec->voices > 1) EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); while (count-- > 0) { unsigned short sval; CHECK_SCHEDULER(); get_user(sval, buf); EMU8000_SMLD_WRITE(emu, sval); buf++; if (rec->voices > 1) { CHECK_SCHEDULER(); get_user(sval, buf); EMU8000_SMRD_WRITE(emu, sval); buf++; } } return 0;}static int emu8k_pcm_silence(snd_pcm_substream_t *subs, int voice, snd_pcm_uframes_t pos, snd_pcm_uframes_t count){ emu8k_pcm_t *rec = subs->runtime->private_data; emu8000_t *emu = rec->emu; snd_emu8000_write_wait(emu, 1); EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos); if (rec->voices > 1) EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos); while (count-- > 0) { CHECK_SCHEDULER(); EMU8000_SMLD_WRITE(emu, 0); if (rec->voices > 1) { CHECK_SCHEDULER(); EMU8000_SMRD_WRITE(emu, 0); } } return 0;}#endif/* * allocate a memory block */static int emu8k_pcm_hw_params(snd_pcm_substream_t *subs, snd_pcm_hw_params_t *hw_params){ emu8k_pcm_t *rec = subs->runtime->private_data; if (rec->block) { /* reallocation - release the old block */ snd_util_mem_free(rec->emu->memhdr, rec->block); rec->block = NULL; } rec->allocated_bytes = params_buffer_bytes(hw_params) + LOOP_BLANK_SIZE * 4; rec->block = snd_util_mem_alloc(rec->emu->memhdr, rec->allocated_bytes); if (! rec->block) return -ENOMEM; rec->offset = EMU8000_DRAM_OFFSET + (rec->block->offset >> 1); /* in word */ /* at least dma_bytes must be set for non-interleaved mode */ subs->dma_buffer.bytes = params_buffer_bytes(hw_params); return 0;}/* * free the memory block */static int emu8k_pcm_hw_free(snd_pcm_substream_t *subs){ emu8k_pcm_t *rec = subs->runtime->private_data; if (rec->block) { int ch; for (ch = 0; ch < rec->voices; ch++) stop_voice(rec, ch); // to be sure if (rec->dram_opened) emu8k_close_dram(rec->emu); snd_util_mem_free(rec->emu->memhdr, rec->block); rec->block = NULL; } return 0;}/* */static int emu8k_pcm_prepare(snd_pcm_substream_t *subs){ emu8k_pcm_t *rec = subs->runtime->private_data; rec->pitch = 0xe000 + calc_rate_offset(subs->runtime->rate); rec->last_ptr = 0; rec->period_pos = 0; rec->buf_size = subs->runtime->buffer_size; rec->period_size = subs->runtime->period_size; rec->voices = subs->runtime->channels; rec->loop_start[0] = rec->offset + LOOP_BLANK_SIZE; if (rec->voices > 1) rec->loop_start[1] = rec->loop_start[0] + rec->buf_size + LOOP_BLANK_SIZE; if (rec->voices > 1) { rec->panning[0] = 0xff; rec->panning[1] = 0x00; } else rec->panning[0] = 0x80; if (! rec->dram_opened) { int err, i, ch; snd_emux_terminate_all(rec->emu->emu); if ((err = emu8k_open_dram_for_pcm(rec->emu, rec->voices)) != 0) return err; rec->dram_opened = 1; /* clear loop blanks */ snd_emu8000_write_wait(rec->emu, 0); EMU8000_SMALW_WRITE(rec->emu, rec->offset); for (i = 0; i < LOOP_BLANK_SIZE; i++) EMU8000_SMLD_WRITE(rec->emu, 0); for (ch = 0; ch < rec->voices; ch++) { EMU8000_SMALW_WRITE(rec->emu, rec->loop_start[ch] + rec->buf_size); for (i = 0; i < LOOP_BLANK_SIZE; i++) EMU8000_SMLD_WRITE(rec->emu, 0); } } setup_voice(rec, 0); if (rec->voices > 1) setup_voice(rec, 1); return 0;}static snd_pcm_uframes_t emu8k_pcm_pointer(snd_pcm_substream_t *subs){ emu8k_pcm_t *rec = subs->runtime->private_data; if (rec->running) return emu8k_get_curpos(rec, 0); return 0;}static snd_pcm_ops_t emu8k_pcm_ops = { .open = emu8k_pcm_open, .close = emu8k_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = emu8k_pcm_hw_params, .hw_free = emu8k_pcm_hw_free, .prepare = emu8k_pcm_prepare, .trigger = emu8k_pcm_trigger, .pointer = emu8k_pcm_pointer, .copy = emu8k_pcm_copy, .silence = emu8k_pcm_silence,};static void snd_emu8000_pcm_free(snd_pcm_t *pcm){ emu8000_t *emu = pcm->private_data; emu->pcm = NULL;}int snd_emu8000_pcm_new(snd_card_t *card, emu8000_t *emu, int index){ snd_pcm_t *pcm; int err; if ((err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm)) < 0) return err; pcm->private_data = emu; pcm->private_free = snd_emu8000_pcm_free; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &emu8k_pcm_ops); emu->pcm = pcm; snd_device_register(card, pcm); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -