/* x11amp - graphically mp3 player.. * Copyright (C) 1998-1999 Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies * * 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 "OSS.h" #define NFRAGS 32 #define min(x,y) (x)<(y)?(x):(y) static gint fd=0; static void *buffer; static gboolean going=FALSE,prebuffer,paused=FALSE,unpause=FALSE,do_pause=FALSE,remove_prebuffer=FALSE; static gint buffer_size,prebuffer_size,blk_size; static gint rd_index=0,wr_index=0; static gint output_time_offset=0,written=0,output_bytes=0; static gint bps,ebps; static gint flush; static gint fragsize,format,channels,frequency,efrequency; static pthread_t buffer_thread; gint abuffer_get_written_time(void) { if(!going) return 0; return (gint)(((gfloat)written*1000)/(gfloat)(bps)); } gint abuffer_get_output_time(void) { audio_buf_info buf_info; gint bytes; if(!fd||!going) return 0; if(!paused) { ioctl(fd,SNDCTL_DSP_GETOSPACE,&buf_info); bytes=output_bytes-((buf_info.fragstotal-buf_info.fragments)*buf_info.fragsize); } else bytes = output_bytes; if(bytes<0) bytes=0; return output_time_offset+(gint)((gfloat)((bytes)*1000.0)/(gfloat)ebps); } gint abuffer_used(void) { if(wr_index>=rd_index) return wr_index-rd_index; return buffer_size-(rd_index-wr_index); } gint abuffer_playing(void) { audio_buf_info buf_info; int bytes; ioctl(fd,SNDCTL_DSP_GETOSPACE,&buf_info); bytes=((buf_info.fragstotal-buf_info.fragments-3)*buf_info.fragsize); if(!abuffer_used()&&bytes<=0) return FALSE; return TRUE; } gint abuffer_free(void) { /* * The (1L<wr_index) return (rd_index-wr_index)-((1L<0) { cnt=min(length,buffer_size-wr_index); memcpy(buffer+wr_index,ptr+off,cnt); wr_index=(wr_index+cnt)%buffer_size; length-=cnt; off=cnt; } } void abuffer_close(void) { wr_index=0; rd_index=0; going=0; pthread_join(buffer_thread,NULL); } void abuffer_flush(gint time) { flush=time; while(flush!=-1) usleep(10000); } void abuffer_pause(short p) { if(p==TRUE) do_pause=TRUE; else unpause=TRUE; } void abuffer_downsample(guchar *ob,guint length,guint speed,guint espeed) { guint nlen,i,off,d; if((format==AFMT_U16_BE||format==AFMT_U16_LE||format==AFMT_S16_BE||format==AFMT_S16_LE)&&channels==2) { gulong *nbuffer,*obuffer,*ptr; obuffer=(gulong *)ob; length>>=2; nlen=(length*espeed)/speed; d=(speed<<8)/espeed; nbuffer=g_malloc(nlen<<2); for(i=0,off=0,ptr=nbuffer;i>8]; off+=d; } output_bytes+=write(fd,nbuffer,nlen<<2); g_free(nbuffer); } else if(((format==AFMT_U16_BE||format==AFMT_U16_LE||format==AFMT_S16_BE||format==AFMT_S16_LE)&&channels==1) || ((format==AFMT_U8||format==AFMT_S8)&&channels==2)) { gushort *nbuffer,*obuffer,*ptr; obuffer=(gushort *)ob; length>>=1; nlen=(length*espeed)/speed; d=(speed<<8)/espeed; nbuffer=g_malloc(nlen<<1); for(i=0,off=0,ptr=nbuffer;i>8]; off+=d; } output_bytes+=write(fd,nbuffer,nlen<<1); g_free(nbuffer); } else { guchar *nbuffer,*obuffer,*ptr; obuffer=ob; nlen=(length*espeed)/speed; d=(speed<<8)/espeed; nbuffer=g_malloc(nlen); for(i=0,off=0,ptr=nbuffer;i>8]; off+=d; } output_bytes+=write(fd,nbuffer,nlen); g_free(nbuffer); } } void *abuffer_loop(void *arg) { gint length,cnt; audio_buf_info abuf_info; gchar *device_name; if(oss_cfg.audio_device!=-1) device_name=g_strdup_printf("/dev/dsp%d",oss_cfg.audio_device); else device_name=g_strdup("/dev/dsp"); fd=open(device_name,O_WRONLY); if(fd==-1) { g_free(buffer); pthread_exit(NULL); } abuffer_set_audio_params(); while(going) { if(abuffer_used()>0&&!paused) { if(!prebuffer) { length=min(blk_size,abuffer_used()); /*printf("%d\n",length);*/ ioctl(fd,SNDCTL_DSP_GETOSPACE,&abuf_info); if(abuf_info.bytes>length) { while(length>0) { cnt=min(length,buffer_size-rd_index); if(frequency==efrequency) output_bytes+=write(fd,buffer+rd_index,cnt); else abuffer_downsample(buffer+rd_index,cnt,frequency,efrequency); rd_index=(rd_index+cnt)%buffer_size; length-=cnt; } } else usleep(10000); if(!abuffer_used()) ioctl(fd,SNDCTL_DSP_POST,0); } else if(abuffer_used()>prebuffer_size) prebuffer=0; else usleep(10000); } else { usleep(10000); /*printf("WARNING: Buffer underrun!\n");*/ } if(do_pause&&!paused) { do_pause=FALSE; paused=TRUE; ioctl(fd,SNDCTL_DSP_GETOSPACE,&abuf_info); rd_index-=(abuf_info.fragstotal-abuf_info.fragments)*abuf_info.fragsize; if(rd_index<0) rd_index+=buffer_size; output_bytes-=(abuf_info.fragstotal-abuf_info.fragments)*abuf_info.fragsize; ioctl(fd,SNDCTL_DSP_RESET,0); } if(unpause&&paused) { unpause=FALSE; close(fd); fd=open(device_name,O_WRONLY); abuffer_set_audio_params(); paused=FALSE; } if(flush!=-1) { /* * This close and open is a work around of a bug that exists in some drivers which * cause the driver to get fucked up by a reset */ ioctl(fd,SNDCTL_DSP_RESET,0); close(fd); fd=open(device_name,O_WRONLY); abuffer_set_audio_params(); output_time_offset=flush; written=(flush/10)*(bps/100); rd_index=wr_index=output_bytes=0; flush=-1; prebuffer=1; } } ioctl(fd,SNDCTL_DSP_RESET,0); close(fd); g_free(buffer); pthread_exit(NULL); } void abuffer_set_audio_params(void) { gint frag,stereo; ioctl(fd,SNDCTL_DSP_RESET,0); frag=(NFRAGS<<16)|fragsize; ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&frag); ioctl(fd,SNDCTL_DSP_SETFMT,&format); stereo=channels-1; ioctl(fd,SNDCTL_DSP_STEREO,&stereo); efrequency=frequency; ioctl(fd,SNDCTL_DSP_SPEED,&efrequency); ioctl(fd,SNDCTL_DSP_GETBLKSIZE,&blk_size); ebps=efrequency*channels; if(format==AFMT_U16_BE||format==AFMT_U16_LE||format==AFMT_S16_BE||format==AFMT_S16_LE) ebps*=2; } gint abuffer_open(AFormat fmt,gint rate,gint nch) { gint fmts; switch(fmt) { case FMT_U8: format=AFMT_U8; break; case FMT_S8: format=AFMT_S8; break; case FMT_U16_LE: format=AFMT_U16_LE; break; case FMT_U16_BE: format=AFMT_U16_BE; break; case FMT_U16_NE: #ifdef AFMT_U16_NE format=AFMT_U16_NE; #else #ifdef WORDS_BIGENDIAN format=AFMT_U16_BE; #else format=AFMT_U16_LE; #endif #endif break; case FMT_S16_LE: format=AFMT_S16_LE; break; case FMT_S16_BE: format=AFMT_S16_BE; break; case FMT_S16_NE: #ifdef AFMT_S16_NE format=AFMT_S16_NE; #else #ifdef WORDS_BIGENDIAN format=AFMT_S16_BE; #else format=AFMT_S16_LE; #endif #endif break; } bps=rate*nch; if(format==AFMT_U16_BE||format==AFMT_U16_LE||format==AFMT_S16_BE||format==AFMT_S16_LE) bps*=2; fragsize=0; while((1L<