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

📄 gus_wave.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 + -