Mercurial > audlegacy-plugins
diff src/Output/CoreAudio/audio.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 | 088092a52fea |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Output/CoreAudio/audio.c Mon Sep 18 01:11:49 2006 -0700 @@ -0,0 +1,612 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2001 Peter Alm, Mikael Alm, Olle Hallnas, + * Thomas Nilsson and 4Front Technologies + * Copyright (C) 1999-2001 Haavard Kvaalen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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. + */ +#include "coreaudio.h" +#include "libaudacious/util.h" +#include <errno.h> +#include <CoreAudio/CoreAudio.h> + +AudioDeviceID device_id; +AudioStreamBasicDescription device_format; +AudioStreamBasicDescription streamDesc; + +//static gint fd = 0; +static float *buffer; +gboolean playing_flag; +static gboolean prebuffer, unpause, do_pause, remove_prebuffer; +static gint device_buffer_size; +static gint buffer_size, prebuffer_size;//, blk_size; +static gint buffer_index = 0; +static gint output_time_offset = 0; +static guint64 written = 0, output_total = 0; +static gint flush; +static gchar *device_name; + +gint sample_multiplier, sample_size; + +gboolean paused; + + +float left_volume, right_volume; +float base_pitch = 0.0; +float user_pitch = 0.0; +int output_buf_length; // length of data in output buffer +short output_buf[OUTPUT_BUFSIZE]; /* buffer used to hold main output to dbfsd */ +short cue_buf[OUTPUT_BUFSIZE]; /* buffer used to hold cue output to dbfsd */ +short conv_buf[OUTPUT_BUFSIZE]; /* buffer used to hold format converted input */ + +/* + * The format of the data from the input plugin + * This will never change during a song. + */ +struct format_info input; + + +/* + * The format we get from the effect plugin. + * This will be different from input if the effect plugin does + * some kind of format conversion. + */ +struct format_info effect; + + +/* + * The format of the data we actually send to the soundcard. + * This might be different from effect if we need to resample or do + * some other format conversion. + */ +struct format_info output; + + +static int osx_calc_bitrate(int osx_fmt, int rate, int channels) +{ + int bitrate = rate * channels; + + // for now we know output is stereo + // fix this later + + if (osx_fmt == FMT_U16_BE || osx_fmt == FMT_U16_LE || + osx_fmt == FMT_S16_BE || osx_fmt == FMT_S16_LE) + { + bitrate *= 2; + } + + //printf("osx_calc_bitrate(): %d\n",bitrate); + + return bitrate; +} + + +static int osx_get_format(AFormat fmt) +{ + int format = 0; + + switch (fmt) + { + case FMT_U16_NE: +#ifdef WORDS_BIGENDIAN + format = FMT_U16_BE; +#else + format = FMT_U16_LE; +#endif + break; + case FMT_S16_NE: +#ifdef WORDS_BIGENDIAN + format = FMT_S16_BE; +#else + format = FMT_S16_LE; +#endif + break; + default: + format = fmt; + break; + } + + return format; +} + + +OSStatus play_callback(AudioDeviceID inDevice, const AudioTimeStamp * inNow, const AudioBufferList * inInputData, const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, const AudioTimeStamp * inOutputTime, void * inClientData) +{ + int i; + long m, n, o; + float * dest, tempfloat; + float * src; + int src_size_bytes; + int src_size_float; + int num_output_samples; + int used_samples; + + src_size_bytes = outOutputData->mBuffers[0].mDataByteSize; + src_size_float = src_size_bytes / sizeof(float); + + num_output_samples = MIN(buffer_index,src_size_float); + + //printf("play_callback(): num_output_samples %d, index %d\n",num_output_samples,buffer_index); + + // if we are prebuffering, zero the buffer + if (prebuffer && (buffer_index < prebuffer_size)) + { + //printf("prebuffering... %d samples left\n",prebuffer_size-buffer_index); + num_output_samples = 0; + } + else + { + prebuffer = FALSE; + } + + src = buffer; + dest = outOutputData->mBuffers[0].mData; + + // copy available data to buffer and apply volume to each channel + for (i = 0; i < num_output_samples/2; i++) + { + //tempfloat = *src; + *dest = (*src) * left_volume; + src++; + dest++; + + *dest = (*src) * right_volume; + src++; + dest++; + } + + // if less than a buffer's worth of data is ready, zero remainder of output buffer + if (num_output_samples != src_size_float) + { + //printf("zeroing %d samples",(src_size_float - num_output_samples)); + + dest = (float*)outOutputData->mBuffers[0].mData + num_output_samples; + + memset(dest,0,(src_size_float - num_output_samples) * sizeof(float)); + } + + // move unwritten data to beginning of buffer + { + dest = buffer; + + for (i = num_output_samples; i < buffer_index; i++) + { + *dest = *src; + dest++; + src++; + } + + output_total += num_output_samples; + buffer_index -= num_output_samples; + } + + + if (flush != -1) + { + osx_set_audio_params(); + output_time_offset = flush; + written = ((guint64)flush * input.bps) / (1000 * sample_size); + buffer_index = 0; + output_total = 0; + + flush = -1; + prebuffer = TRUE; + } + + //printf("\n"); + + return 0; +} + + +static void osx_setup_format(AFormat fmt, int rate, int nch) +{ + //printf("osx_setup_format(): fmt %d, rate %d, nch %d\n",fmt,rate,nch); + + effect.format.xmms = osx_get_format(fmt); + effect.frequency = rate; + effect.channels = nch; + effect.bps = osx_calc_bitrate(fmt, rate, nch); + + output.format.osx = osx_get_format(fmt); + output.frequency = rate; + output.channels = nch; + + osx_set_audio_params(); + + output.bps = osx_calc_bitrate(output.format.osx, output.frequency,output.channels); +} + + +gint osx_get_written_time(void) +{ + gint retval; + + if (!playing_flag) + { + retval = 0; + } + else + { + retval = (written * sample_size * 1000) / effect.bps; + retval = (int)((float)retval / user_pitch); + } + + //printf("osx_get_written_time(): written time is %d\n",retval); + + return retval; +} + + +gint osx_get_output_time(void) +{ + gint retval; + + retval = output_time_offset + ((output_total * sample_size * 1000) / output.bps); + retval = (int)((float)retval / user_pitch); + + //printf("osx_get_output_time(): time is %d\n",retval); + + return retval; +} + + +gint osx_playing(void) +{ + gint retval; + + retval = 0; + + if (!playing_flag) + { + retval = 0; + } + else + { + if (buffer_index == 0) + { + retval = FALSE; + } + else + { + retval = TRUE; + } + } + + //printf("osx_playing(): playing is now %d\n",playing_flag); + + return retval; +} + + +gint osx_free(void) +{ + gint bytes_free; + + if (remove_prebuffer && prebuffer) + { + prebuffer = FALSE; + remove_prebuffer = FALSE; + } + + if (prebuffer) + { + remove_prebuffer = TRUE; + } + + // get number of free samples + bytes_free = buffer_size - buffer_index; + + // adjust for mono + if (input.channels == 1) + { + bytes_free /= 2; + } + + // adjust by pitch conversion; + bytes_free = (int)((float)bytes_free * base_pitch * user_pitch); + + // convert from number of samples to number of bytes + bytes_free *= sample_size; + + return bytes_free; +} + + +void osx_write(gpointer ptr, int length) +{ + int count, offset = 0; + int error; + float tempfloat; + float * dest; + short * src, * tempbuf; + int i; + int num_samples; + + //printf("oss_write(): lenght: %d \n",length); + + remove_prebuffer = FALSE; + + // //printf("written is now %d\n",(gint)written); + + // get number of samples + num_samples = length / sample_size; + + // update amount of samples received + written += num_samples; + + // step through audio + while (num_samples > 0) + { + // get # of samples to write to the buffer + count = MIN(num_samples, osx_free()/sample_size); + + src = ptr+offset; + + if (dbconvert((char*)src,count * sample_size) == -1) + { + //printf("dbconvert error %d\n",errno); + } + else + { + src = output_buf; + dest = (float*)(buffer + buffer_index); + + //printf("output_buf_length is %d\n",output_buf_length); + + for (i = 0; i < output_buf_length; i++) + { + tempfloat = ((float)*src)/32768.0; + *dest = tempfloat; + dest++; + src++; + } + + buffer_index += output_buf_length; + } + + if (buffer_index > buffer_size) + { + //printf("BUFFER_INDEX > BUFFER_SIZE!!!!\n"); + exit(0); + } + + num_samples -= count; + offset += count; + } + + //printf("buffer_index is now %d\n\n",buffer_index); +} + + +void osx_close(void) +{ + //printf("osx_close(): playing_flag is %d\n",playing_flag); + + if (!playing_flag) + { + return; + } + + playing_flag = 0; + + // close audio device + AudioDeviceStop(device_id, play_callback); + AudioDeviceRemoveIOProc(device_id, play_callback); + + g_free(device_name); + + //printf("osx_close(): playing_flag is now %d\n",playing_flag); +} + + +void osx_flush(gint time) +{ + //printf("osx_flush(): %d\n",time); + + flush = time; + + while (flush != -1) + { + xmms_usleep(10000); + } +} + + +void osx_pause(short p) +{ + //printf("osx_pause(): %d\n",p); + + if (p == TRUE) + { + if (AudioDeviceStop(device_id, play_callback)) + { + //printf("failed to stop audio device.\n"); + } + + //printf("PAUSED!\n"); + } + else + { + if (AudioDeviceStart(device_id, play_callback)) + { + //printf("failed to start audio device.\n"); + } + + //printf("UNPAUSED!\n"); + } + + paused = p; +} + + +void osx_set_audio_params(void) +{ + int stereo_multiplier, format_multiplier; + int frag, stereo, ret; + struct timeval tv; + fd_set set; + + //printf("osx_set_audio_params(): fmt %d, freq %d, nch %d\n",output.format.osx,output.frequency,output.channels); + + // set audio format + + // set num channels + + switch (input.channels) + { + case 1: stereo_multiplier = 2; break; + case 2: stereo_multiplier = 1; break; + default: stereo_multiplier = 1; break; + } + + switch (input.format.xmms) + { + case FMT_U8: + case FMT_S8: + format_multiplier = 2; + sample_size = 1; + break; + case FMT_S16_LE: + case FMT_S16_BE: + case FMT_S16_NE: + format_multiplier = 1; + sample_size = 2; + break; + default: format_multiplier = 1; break; + } + + sample_multiplier = stereo_multiplier * format_multiplier; + + base_pitch = input.frequency / device_format.mSampleRate; + + //printf("sample multiplier is now %d, base pitch %.2f\n",sample_multiplier,base_pitch); +} + + +gint osx_open(AFormat fmt, gint rate, gint nch) +{ + char s[32]; + long m; + long size; + char device_name[128]; + + //printf("\nosx_open(): fmt %d, rate %d, nch %d\n",fmt,rate,nch); + + // init conversion variables + base_pitch = 1.0; + user_pitch = 1.0; + + // open audio device + + size = sizeof(device_id); + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device_id)) + { + //printf("failed to open default audio device"); + return -1; + } + + //printf("opened audio device\n"); + + size = 128; + + if (AudioDeviceGetProperty(device_id,1,0,kAudioDevicePropertyDeviceName,&size,device_name)) + { + //printf("could not get device name\n"); + return -1; + } + + //printf("device name is: \"%s\"\n",device_name); + + size = sizeof(device_format); + + if (AudioDeviceGetProperty(device_id, 0, 0, kAudioDevicePropertyStreamFormat, &size, &device_format)) + { + //printf("failed to get audio format!\n"); + return -1; + } + + //fprintf(stderr, "got format: sample rate %f, %ld channels and %ld-bit sample\n", + // device_format.mSampleRate,device_format.mChannelsPerFrame,device_format.mBitsPerChannel); + + if (device_format.mFormatID != kAudioFormatLinearPCM) + { + //printf("audio format isn't PCM\n"); + return -1; + } + + //printf("format is PCM\n"); + + input.format.xmms = fmt; + input.frequency = rate; + input.channels = nch; + input.bps = osx_calc_bitrate(osx_get_format(fmt),rate,nch); + + osx_setup_format(osx_get_format(fmt),device_format.mSampleRate,device_format.mChannelsPerFrame); + + //set audio buffer size + { + device_buffer_size = 4096 * sizeof(float); + size = sizeof(gint); + + if (AudioDeviceSetProperty(device_id,0,0,0,kAudioDevicePropertyBufferSize,size,&device_buffer_size)) + { + //printf("failed to set device buffer size\n"); + } + + //printf("buffer size set to %d\n",device_buffer_size); + } + + buffer_size = 11 * 4096; + prebuffer_size = 4096; + + buffer = (float *) g_malloc0(buffer_size*sizeof(float)); + + //printf("created buffer of size %d, prebuffer is %d\n",buffer_size,prebuffer_size); + + flush = -1; + prebuffer = TRUE; + + buffer_index = output_time_offset = written = output_total = 0; + + paused = FALSE; + + do_pause = FALSE; + unpause = FALSE; + remove_prebuffer = FALSE; + + playing_flag = 1; + + if (AudioDeviceAddIOProc(device_id, play_callback, NULL)) + { + //printf("failed to add IO Proc callback\n"); + osx_close(); + return -1; + } + + //printf("added callback\n"); + + if (AudioDeviceStart(device_id,play_callback)) + { + osx_close(); + //printf("failed to start audio device.\n"); + exit(0); + } + + //printf("started audio device\n"); + + return 1; +}
