diff src/Output/CoreAudio/dbconvert.c @ 0:13389e613d67 trunk

[svn] - initial import of audacious-plugins tree (lots to do)
author nenolod
date Mon, 18 Sep 2006 01:11:49 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Output/CoreAudio/dbconvert.c	Mon Sep 18 01:11:49 2006 -0700
@@ -0,0 +1,524 @@
+/*
+
+  Author:  Bob Dean
+  Copyright (c) 1999 - 2004
+
+  The functionality in this file is modified from DBAudio_Write.c. 
+  part of the DBMix project which is also released under the GPL.
+  It is used here both as licensed under the GPL and additionally 
+  by permission of the original author (which is me).
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public Licensse as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+ 
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ 
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/shm.h>
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+	
+#include "coreaudio.h"
+#include "dbaudiolib.h"
+
+	extern int errno;
+	
+	extern gboolean paused;
+	extern float left_volume, right_volume;
+
+	extern float base_pitch;
+	extern float user_pitch;
+	
+
+	float local_pitch;
+	float sample1, sample2;
+	float float_index;
+	
+	extern signed short output_buf[];  /* buffer used to hold main output to dbfsd */
+	extern signed short cue_buf[];     /* buffer used to hold cue output to dbfsd */
+	extern signed short conv_buf[];    /* buffer used to hold format converted input */
+	extern int output_buf_length;
+	
+	int outlen;
+	int sampleindex;
+	int num_channels;
+	int format;
+
+
+	extern struct format_info input;
+
+	
+	/*
+	  dbconvert - given a buf of length len, write the data to the
+	  channel associated with this instance.
+
+	  On success, the number of bytes written is returned. Otherwise
+	  -1, or FAILURE, is returned, and errno is set accordingly.
+
+	  Hopefully this function returns values in the same fashion
+	  as other basic I/O functions such as read() and write()
+
+	  Variables: 
+	  count is the number of bytes written during a loop iteration
+	  totalcount is the total number of bytes written
+	  left_gain and right_gain are percentages used to adjust the 
+	  output signal volume
+	  tempbuf is a temporary pointer used in the volume operation
+	  temp_chbuf is a pointer to the buffer to be written to during
+	  shared memory mode.
+	*/
+
+	int dbconvert(char* buf, int len)
+	{
+		int left_gain,right_gain;
+		int left_cue_gain, right_cue_gain;
+		signed short * tempbuf, *tempbuf2, * tempoutbuf;
+		char * tempcharbuf;
+		int incr_flag, count;
+		int intindex;
+		float buflen, gain1, gain2;
+		int   index1,index2, output_sample;
+		int i;
+		int stereo_multiplier,format_multiplier;
+		int tempsize;
+		int sampler_flag;
+		//enum sampler_state_e local_sampler_state;
+
+		/* check parameters */
+		if (buf == NULL) {errno = ERROR_BAD_PARAM; return FAILURE;}
+		//if (ch == NULL)  {errno = ERROR_NOT_INITIALIZED; return FAILURE;}
+		if (len < 0)     {errno = ERROR_BAD_PARAM; return FAILURE;}
+	
+		//DBAudio_Handle_Message_Queue();
+		
+		/* remember sampler state as it may change during
+		   the course of the function */
+#ifdef COMPILE_SAMPLER
+		local_sampler_state = ch->sampler_state;
+#endif
+
+		if (paused)
+		{
+			//printf("convert: pauseed\n");
+			return 0;
+		}
+
+		/* get pitch */
+		local_pitch = base_pitch * user_pitch;
+
+		//printf("convert: local pitch is %.2f, base %.2f user %.2f\n",local_pitch,base_pitch,user_pitch);
+		buflen = len / 2.0;
+	
+		//printf("convert: buflen %.2f len is %d\n",buflen,len);
+
+		/* calculate buffer space needed to convert the data to 
+		   44.1kHz 16bit stereo*/
+		switch (input.channels)
+		{
+			case 1:  stereo_multiplier = 2; break;
+			case 2:  stereo_multiplier = 1; break;
+			default: errno = ERROR_BAD_NUMCH; return FAILURE;
+		}
+		
+		//printf("convert: format %d, %d %d %d %d\n",input.format.xmms,FMT_U8,FMT_S8,FMT_S16_LE,FMT_S16_BE);
+
+		switch (input.format.xmms)
+		{
+			case FMT_U8:     format_multiplier = 2; break;
+			case FMT_S8:     format_multiplier = 2; break;
+			case FMT_S16_LE: format_multiplier = 1; break;
+			case FMT_S16_BE: format_multiplier = 1; break;
+			case FMT_S16_NE: format_multiplier = 1; break;
+			default: errno = ERROR_BAD_FORMAT; return FAILURE;
+		}
+		
+		/* return error if the needed output space is greater than the
+		   output buffer */
+		if (ceil((buflen * (float)stereo_multiplier * 
+				  (float)format_multiplier) / local_pitch) > (float)(OUTPUT_BUFSIZE))
+		{
+			errno = ERROR_TOO_MUCH_DATA; 
+			return FAILURE;
+		}
+		
+		/* init local variables */
+		intindex = 0;
+		incr_flag = 0;
+		sampleindex = 0;
+		gain1 = gain2 = 0.0;
+		sample1 = sample2 = 0.0;
+		sampler_flag = 0;
+
+		left_gain = (int)(128.0 * left_volume);
+		right_gain = (int)(128.0 * right_volume);
+
+	
+#ifdef COMPILE_CUE
+		left_cue_gain = ch->cue_left_gain;
+		right_cue_gain = ch->cue_right_gain;
+
+		/* calculate gain percentages */
+		if (ch->mute == TRUE)
+		{
+			left_gain = right_gain = 0;
+		}
+		else
+		{
+			left_gain = ch->left_gain * sysdata->left_balance;
+			right_gain = ch->right_gain * sysdata->right_balance;
+	
+			/* cut volume if mic is being used */
+			if (sysdata->talkover_enabled && !(MIC_ENABLED))
+			{
+				left_gain = left_gain >> DB_TALKOVER_DIVISOR_POWER;
+				right_gain = right_gain >> DB_TALKOVER_DIVISOR_POWER;
+			}
+		}
+#endif
+
+#ifdef COMPILE_SAMPLER
+		switch (local_sampler_state)
+		{
+			case SAMPLER_PLAY_SINGLE:
+			case SAMPLER_PLAY_LOOP:				
+
+				if (ch->sampler_size == 0)
+				{
+					ch->sampler_state = SAMPLER_OFF;
+					len = 0;
+					goto done;
+				}
+				
+				/* tempsize - amount of data available in buffer to read */
+				tempsize = (ch->sampler_endoffset - ch->sampler_readoffset);
+
+				sampler_flag = 1;
+
+				/* if we are in loop mode and loop over end of buffer, 
+                   get data from start of buffer */
+				if ((tempsize < len) && (local_sampler_state == SAMPLER_PLAY_LOOP))
+				{
+					/* copy portion at end of buffer */
+					memcpy(conv_buf,(ch->sampler_buf + ch->sampler_readoffset),tempsize);
+					/* copy portion at beginning of buffer */
+					memcpy(conv_buf+tempsize,ch->sampler_buf+ch->sampler_startoffset,(len - tempsize));
+					/* update variables */
+					/* read offset is now amount to write, minus the overflow, plus the startoffset */
+					ch->sampler_readoffset = len - tempsize + ch->sampler_startoffset;
+					tempsize = len;
+				}
+				else
+				{
+					/* if we are in simgle play mode and out of data, reset state
+                       and exit */
+					if ((tempsize <= 0) && (local_sampler_state == SAMPLER_PLAY_SINGLE))
+					{
+						ch->sampler_state = SAMPLER_READY;
+						goto done;
+					}
+
+					/* get full buffers worth of data from somewhere in middle of 
+                       sampler buffer  */
+					if (tempsize > len) tempsize = len;
+
+					memcpy(conv_buf,(ch->sampler_buf + ch->sampler_readoffset),tempsize);
+
+					ch->sampler_readoffset += tempsize;
+				}
+
+
+				/* update function state variables */
+				buflen = (tempsize / 2);
+				tempbuf = conv_buf;
+
+				break;
+			default:
+#endif
+				{
+					/* convert input data into 44.1 KHz 16 bit stereo */
+					tempbuf = (signed short *) buf;
+					
+					/* convert mono input to stereo */
+					if (input.channels == 1)
+					{
+						//printf("convert: data is mono\n");
+
+						tempbuf2 = conv_buf;
+						
+						if ((input.format.xmms == FMT_U8) || (input.format.xmms == FMT_S8))
+						{
+							tempcharbuf = buf;
+							
+							for (i = 0; i < buflen*2.0; i++)
+							{
+								*tempbuf2 = *tempcharbuf; tempbuf2++;
+								*tempbuf2 = *tempcharbuf; tempbuf2++; tempcharbuf++;
+							}
+						} 
+						else 
+						{
+							for (i = 0; i < buflen; i++)
+							{
+								*tempbuf2 = *tempbuf; tempbuf2++;
+								*tempbuf2 = *tempbuf; tempbuf2++; tempbuf++;
+							}
+						}
+						
+						buflen *=2.0;
+						tempbuf = conv_buf;
+					}
+					else
+					{
+						//printf("convet: data is stereo\n");
+					}
+					
+		//printf("convert: buflen %.2f\n",buflen);
+
+
+					/* convert 8 bit input to 16 bit input */
+					if ((input.format.xmms != FMT_S16_LE) && (input.format.xmms != FMT_S16_BE) 
+						&& (input.format.xmms != FMT_S16_NE))
+					{
+						switch (input.format.xmms)
+						{
+							case FMT_U8:
+							{
+								//printf("convert: converting unsigned 8 bit\n");
+								
+								tempbuf2 = conv_buf;
+								buflen *= 2.0;
+								
+								/* if data was mono, then it is already in conv_buf */
+								if (input.channels == 1)
+								{
+									for (i = 0; i < buflen; i++)
+									{
+										*tempbuf = (*tempbuf2 - 127) << 8;
+										tempbuf++; tempbuf2++;
+									}
+								}
+								else
+								{   /* data is 8 bit stereo, and is in buf not conv_buf*/
+									tempcharbuf = buf;
+									for (i = 0; i < len; i++)
+									{
+										*tempbuf = (*tempcharbuf - 127) << 8;
+										tempbuf++; tempcharbuf++;
+									}			
+								}
+								
+								tempbuf = conv_buf;
+								break;
+							}
+							case FMT_S8:
+							{
+								//printf("convert: converting signed 8 bit\n");
+
+								tempbuf2 = conv_buf;
+								buflen *= 2.0;
+								
+								/* if data was mono, then it is already in conv_buf */
+								if (input.channels == 1)
+								{
+									for (i = 0; i < buflen; i++)
+									{
+										*tempbuf = *tempbuf2 << 8;
+										tempbuf++; tempbuf2++;
+									}
+								}
+								else
+								{   /* data is 8 bit stereo, and is in buf not conv_buf*/
+									tempcharbuf = buf;
+									for (i = 0; i < len; i++)
+									{
+										*tempbuf = *tempcharbuf << 8;
+										tempbuf++; tempcharbuf++;
+									}			
+								}
+								
+								tempbuf = conv_buf;
+								break;
+							}
+							default: 
+							{
+								errno = ERROR_BAD_FORMAT; 
+								//ch->writing = 0;
+								return FAILURE;
+							}
+						}
+					}	
+				} /* end default case*/
+
+		//printf("convert: buflen %.2f\n",buflen);
+
+
+
+#ifdef COMPILE_SAMPLER
+		} /* end switch sampler_state */
+
+		/* copy buffer to sample buffer if sampler state is record */
+		if (local_sampler_state == SAMPLER_RECORD)
+		{
+			tempsize = 0;
+
+			/* get amount of data to copy */
+			if ((ch->sampler_size + (buflen * 2)) > ch->sampler_bufsize)
+			{
+				tempsize = ch->sampler_bufsize - ch->sampler_size;
+			}
+			else
+			{
+				tempsize = (buflen * 2);
+			}
+
+			/* change state if buffer is full */
+			if (tempsize == 0)
+			{
+				ch->sampler_state = SAMPLER_READY;
+			}
+			
+			/* copy data */
+			memcpy(((ch->sampler_buf) + (ch->sampler_size)),tempbuf,tempsize);
+
+			/* update sampler state variables */
+			ch->sampler_size += tempsize;
+			ch->sampler_endoffset = ch->sampler_size;
+		}
+#endif
+
+		if (local_pitch == 1.0)
+		{
+			//printf("convert: pitch optimization buflen %.2f *2 %.2f\n",buflen,buflen*2);
+			//printf("tempbuf is 0x%x, output_buf is 0x%x\n",tempbuf,output_buf);
+			memcpy(output_buf,tempbuf,buflen*2);
+			
+			outlen = buflen*2;
+			tempbuf = output_buf;
+			output_buf_length = buflen;
+			
+			goto done;
+		}
+
+		/* calculate pitch shifted signal using basic linear interpolation
+		   the theory is this:
+		   you have two known samples, and want to calculate the value of a new sample
+		   in between them.  The new sample will contain a percentage of the first sample
+		   and a percentage of the second sample.  These percentages are porportional
+		   to the distance between the new sample and each of the knwon samples.
+		   The "position" of the new sample is determined by the float index */
+
+		tempoutbuf = output_buf;
+
+		while (intindex < buflen)
+		{
+			/* calculate sample percentages (amplitude) */
+			intindex = floor(float_index);
+			gain2 = float_index - intindex;
+			gain1 = 1.0 - gain2;
+
+			/* get index of first sample pair */
+			intindex = intindex << 1;
+			
+			/* check incr_flag to see if we should be operatiing 
+			   on the left or right channel sample */
+			if (incr_flag) 
+			{ 
+				float_index += local_pitch; 
+				incr_flag = 0; 
+				intindex++;
+			}
+			else
+			{
+				incr_flag = 1;
+			}
+			
+			index1 = intindex;
+			
+			/* get the first "known" sample*/
+			sample1 = tempbuf[index1];
+			index2 = index1 + 2; 
+			
+			/* get the second "known" sample */
+			if (index2 < (buflen))
+			{
+				sample2 = tempbuf[index2];
+			}
+			else
+				/* if index2 is beyond the length of the input buffer,
+                   then cheat to prevent audio pops/snaps/etc */
+			{
+				*tempoutbuf = sample1;
+				sampleindex++;
+				break;
+			}
+			
+			/* create the new sample */
+			output_sample = (((float)sample1 * gain1) + ((float)sample2 * gain2));
+			
+			if (output_sample > 32767)  {output_sample = 32767;}
+			if (output_sample < -32767)	{output_sample = -32767;}
+			
+			*tempoutbuf = output_sample;
+			tempoutbuf++;
+			
+			sampleindex++;
+		}
+
+		/* update global variables */
+		outlen = (sampleindex-1) << 1;
+		
+		float_index = float_index - floor(float_index);
+		
+		tempbuf = output_buf;
+
+		output_buf_length = sampleindex - 1;
+		
+		/* 	if (outlen < PIPE_BUF)
+			{errno = ERROR_TOO_LITTLE_DATA; return FAILURE;} */
+
+	apply_gain:
+
+	
+	done:
+
+		//ch->writing = 0;
+
+		if (sampler_flag)
+		{
+			return 0;
+		}
+		else
+		{
+			return len;
+		}
+	}
+
+#ifdef __cplusplus
+}
+#endif
+