Mercurial > audlegacy-plugins
view src/CoreAudio/audio.c @ 1080:b51246bc3fb3 trunk
[svn] - wavpack: transition to plugin API v2
author | nenolod |
---|---|
date | Thu, 24 May 2007 16:32:36 -0700 |
parents | 755a71ca3c92 |
children | aee4ebea943a |
line wrap: on
line source
/* 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 "audacious/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; static int (*osx_convert_func) (void **data, int length); 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; } return bitrate; } static gboolean osx_format_is_neutral(AFormat fmt) { gboolean ret = FALSE; switch (fmt) { case FMT_U16_NE: case FMT_S16_NE: case FMT_U8: case FMT_S8: ret = TRUE; break; default: break; } return ret; } 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; } static int osx_get_conv_format(AFormat fmt) { int format = 0; switch (fmt) { case FMT_U16_LE: #ifdef WORDS_BIGENDIAN format = FMT_U16_BE; #else format = FMT_U16_LE; #endif break; case FMT_U16_BE: #ifdef WORDS_BIGENDIAN format = FMT_U16_LE; #else format = FMT_U16_BE; #endif break; case FMT_S16_LE: #ifdef WORDS_BIGENDIAN format = FMT_S16_BE; #else format = FMT_S16_LE; #endif break; case FMT_S16_BE: #ifdef WORDS_BIGENDIAN format = FMT_S16_LE; #else format = FMT_S16_BE; #endif break; 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; if (osx_convert_func != NULL) osx_convert_func(&ptr, length); // 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); /* Free audio buffer */ g_free(buffer); } void osx_flush(gint time) { //printf("osx_flush(): %d\n",time); flush = time; while (flush != -1) { xmms_usleep(10000); } } void osx_pause(short p) { if (p == TRUE) AudioDeviceStop(device_id, play_callback); else AudioDeviceStart(device_id, play_callback); 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"); if (osx_format_is_neutral(fmt) == FALSE) osx_convert_func = osx_get_convert_func(fmt, osx_get_conv_format(fmt)); else osx_convert_func = NULL; 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; }