Mercurial > audlegacy-plugins
view src/CoreAudio/audio.c @ 972:cf7021ca4e7b trunk
[svn] Add lastfm:// transport, an abstract VFS class which derives from curl
to provide lastfm radio support. Written by majeru with some cleanups
by me. Most last.fm metadata support isn't yet implemented, however, and
will need to be done by majeru. ;)
| author | nenolod |
|---|---|
| date | Sun, 22 Apr 2007 04:16:08 -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; }
