// sndstretch_xmms.c // // sndstretch_xmms - xmms-output plugin for adjusting // pitch and speed of s16le data // Copyright (C) 2001 Florian Berger // Email: florian.berger@jk.uni-linz.ac.at // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License Version 2 as // published by the Free Software Foundation; // // 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., 675 Mass Ave, Cambridge, MA 02139, USA. // // #include #include "sndstretch_xmms.h" #include "sndstretch_xmms-logo.xpm" #include "FB_logo.xpm" #include "sndstretch.h" #include #include #include #include #include #include #include #include #define SNDSTRETCH_VERSION_STRING "0.7" #define SS sndstretch_var #ifdef COMPILE_AS_OUTPUT OutputPlugin sndstretch_op = { NULL, /* Filled in by xmms */ NULL, /* Filled in by xmms */ "SndStretch - sound stretcher ", /* The description that is shown in the preferences box */ sndstretch_init, // NULL, /* void *init(void) */ sndstretch_about, /* Show the about box */ sndstretch_config, /* Show the configuration dialog */ sndstretch_get_volume, sndstretch_set_volume, sndstretch_open_audio, /* Open the device, if the device can't handle the given parameters the plugin is responsible for downmixing the data to the right format before outputting it */ sndstretch_write_audio, /* The input plugin calls this to write data to the output buffer */ sndstretch_close_audio, /* No comment... */ sndstretch_flush, /* Flush the buffer and set the plugins internal timers to time */ sndstretch_pause, /* Pause or unpause the output */ sndstretch_buffer_free, /* Return the amount of data that can be written to the buffer, two calls to this without a call to write_audio should make the plugin output audio directly */ sndstretch_buffer_playing, /* Returns TRUE if the plugin currently is playing some audio, otherwise return FALSE */ sndstretch_output_time, /* Return the current playing time */ sndstretch_written_time /* Return the length of all the data that has been written to buffer */ }; OutputPlugin *get_oplugin_info(void) { return &sndstretch_op; } #endif /* COMPILE_AS_OUTPUT */ #ifdef COMPILE_AS_EFFECT EffectPlugin sndstretch_ep = { NULL, /* Filled in by xmms */ NULL, /* Filled in by xmms */ "SndStretch - sound stretcher ", /* The description that is shown in the preferences box */ sndstretch_init, /* Called when the plugin is loaded */ // void (*cleanup) (void); /* Called when the plugin is unloaded */ NULL, sndstretch_about, /* Show the about box */ sndstretch_config, /* Show the configure box */ // int (*mod_samples) (gpointer *data, gint length, AFormat fmt, gint srate, gint nch); /* Modify samples */ sndstretch_mod_samples, // void (*query_format) (AFormat *fmt,gint *rate, gint *nch); NULL }; EffectPlugin *get_eplugin_info(void) { return &sndstretch_ep; } #endif /* COMPILE_AS_EFFECT */ static struct { int handle; // file handle int fragsize; int chnr; int paused; int time_offs; int fmtsize; int fmt; int sampfreq; int written; int bpsec; int vol_l,vol_r; int going; double pitch; double speed; double scale; int short_overlap; int volume_corr; GtkObject * pitch_adj; GtkObject * speed_adj; GtkObject * scale_adj; } sndstretch_var; static const char sndstretch_about_text[] = "Sound Stretcher Plugin\n\n \ By Florian Berger\n \n\n (c) 2001"; static const char sndstretch_GPL_text[] = "\ 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.\n\n\ 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.\n\n\ 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."; static const char sndstretch_copy_text[] = "Copyright (C) 2001\n\ Florian Berger\n\n\ http://www.geocities.com/harpin_floh/home.html"; static const char sndstretch_title_text[] = "SndStretch xmms - "SNDSTRETCH_VERSION_STRING; GtkWidget * sndstretch_about_dialog = NULL; GtkWidget * sndstretch_config_dialog = NULL; static gint sndstretch_about_destroy_cb(GtkWidget * w, GdkEventAny * e, gpointer data) { gtk_widget_destroy(sndstretch_about_dialog); sndstretch_about_dialog = NULL; return TRUE; } static void sndstretch_about_ok_cb(GtkButton * button, gpointer data) { gtk_widget_destroy(GTK_WIDGET(sndstretch_about_dialog)); sndstretch_about_dialog = NULL; } void sndstretch_about(void) { GtkWidget * vbox, * text, * scrolltext, * button; GtkWidget * titlelabel, * copylabel; GdkPixmap * logopix; GdkBitmap * logomask; GtkWidget * logo; GdkPixmap * FBlogopix; GdkBitmap * FBlogomask; GtkWidget * FBlogo; GtkWidget * copyhbox, * copy_rbox, * copy_lbox; if (sndstretch_about_dialog != NULL) return; sndstretch_about_dialog = gtk_dialog_new(); gtk_widget_show(sndstretch_about_dialog); /* title logo */ logopix = gdk_pixmap_create_from_xpm_d(sndstretch_about_dialog->window, &logomask, NULL, (gchar **) sndstretch_xmms_logo_xpm); logo = gtk_pixmap_new(logopix,logomask); /* FB-logo */ FBlogopix = gdk_pixmap_create_from_xpm_d(sndstretch_about_dialog->window, &FBlogomask, NULL, (gchar **) FB_logo_xpm); FBlogo = gtk_pixmap_new(FBlogopix,FBlogomask); gtk_signal_connect(GTK_OBJECT(sndstretch_about_dialog), "destroy", GTK_SIGNAL_FUNC(sndstretch_about_destroy_cb), NULL); gtk_window_set_title(GTK_WINDOW(sndstretch_about_dialog), "About SndStretch"); /* labels */ titlelabel = gtk_label_new(sndstretch_title_text); copylabel = gtk_label_new(sndstretch_copy_text); gtk_label_set_justify(GTK_LABEL(copylabel),GTK_JUSTIFY_LEFT); copy_lbox = gtk_hbox_new(FALSE,0); copy_rbox = gtk_hbox_new(FALSE,0); gtk_box_pack_end (GTK_BOX(copy_lbox), FBlogo, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(copy_rbox), copylabel, FALSE, TRUE, 0); copyhbox = gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(copyhbox), copy_lbox, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(copyhbox), copy_rbox, TRUE, TRUE, 5); vbox = gtk_vbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(sndstretch_about_dialog)->vbox), vbox, TRUE, TRUE, 5); scrolltext = gtk_scrolled_window_new(NULL,NULL); text = gtk_text_new(NULL, NULL); gtk_text_set_editable(GTK_TEXT(text), FALSE); gtk_text_set_word_wrap(GTK_TEXT(text), TRUE); gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL, sndstretch_GPL_text, strlen(sndstretch_GPL_text)); scrolltext = gtk_scrolled_window_new(NULL,NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolltext), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(scrolltext),text); gtk_box_pack_start(GTK_BOX(vbox), logo, FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(vbox), titlelabel, FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(vbox), copyhbox, FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(vbox), scrolltext, TRUE, TRUE, 5); gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); gtk_widget_set_usize (scrolltext,-1,110); button = gtk_button_new_with_label("Close"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(sndstretch_about_dialog)->action_area), button, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sndstretch_about_ok_cb), NULL); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_widget_grab_default(button); gtk_widget_show(button); // gtk_widget_set_usize (sndstretch_about_dialog,-1,-1); gtk_widget_show_all(sndstretch_about_dialog); } static void speed_change_cb(GtkAdjustment * adj, gpointer data) { #ifdef DEBUG fprintf(stderr,"speed_change_cb\n"); #endif SS.speed = pow( 2.0, GTK_ADJUSTMENT(adj)->value / (GTK_ADJUSTMENT(adj)->upper-10) ); } static void pitch_change_cb(GtkAdjustment * adj, gpointer data) { #ifdef DEBUG fprintf(stderr,"pitch_change_cb\n"); #endif SS.pitch = pow( 2.0, GTK_ADJUSTMENT(adj)->value / (GTK_ADJUSTMENT(adj)->upper-10) ); gtk_adjustment_set_value( GTK_ADJUSTMENT(SS.scale_adj), (GTK_ADJUSTMENT(SS.scale_adj)->upper-10.0)*log(SS.pitch)/log(2.0) ); } static void scale_change_cb(GtkAdjustment * adj, gpointer data) { double speed_eff; #ifdef DEBUG fprintf(stderr,"scale_change_cb\n"); #endif SS.scale = pow( 2.0, GTK_ADJUSTMENT(adj)->value / (GTK_ADJUSTMENT(adj)->upper-10) ); speed_eff= SS.speed/SS.pitch; SS.pitch = SS.scale; SS.speed = speed_eff*SS.scale; if (SS.speed>2.0) SS.speed=2.0; if (SS.speed<0.5) SS.speed=0.5; gtk_adjustment_set_value( GTK_ADJUSTMENT(SS.speed_adj), (GTK_ADJUSTMENT(SS.speed_adj)->upper-10.0)*log(SS.speed)/log(2.0) ); gtk_adjustment_set_value( GTK_ADJUSTMENT(SS.pitch_adj), (GTK_ADJUSTMENT(SS.pitch_adj)->upper-10.0)*log(SS.pitch)/log(2.0) ); } static void overlap_toggle_cb(GtkToggleButton *butt, gpointer user_data) { fprintf(stderr,"overlap_toggle_cb\n"); SS.short_overlap = gtk_toggle_button_get_active( butt ); } static void volume_toggle_cb(GtkToggleButton *butt, gpointer user_data) { SS.volume_corr = gtk_toggle_button_get_active( butt ); } static void sndstretch_config_logobutton_cb(GtkButton * button, gpointer data) { sndstretch_about(); } static gint sndstretch_config_destroy_cb(GtkWidget * w, GdkEventAny * e, gpointer data) { ConfigFile * cfgfile; /* free(vbox); free(speed_frame); free(pitch_frame); free(scale_frame); free(speed_hbox); free(pitch_hbox); free(scale_hbox); free(speed_scale); free(pitch_scale); free(scale_scale); free(speed_spin); free(pitch_spin); free(scale_spin);*/ if ( (cfgfile = xmms_cfg_open_default_file()) ) { xmms_cfg_write_double( cfgfile, "sndstretch", "pitch", SS.pitch ); xmms_cfg_write_double( cfgfile, "sndstretch", "speed", SS.speed ); xmms_cfg_write_boolean( cfgfile, "sndstretch", "short_overlap", SS.short_overlap ); xmms_cfg_write_boolean( cfgfile, "sndstretch", "volume_corr", SS.volume_corr ); xmms_cfg_write_default_file( cfgfile ); xmms_cfg_free( cfgfile ); } gtk_widget_destroy(sndstretch_config_dialog); sndstretch_config_dialog = NULL; return TRUE; } void sndstretch_config(void) { GtkWidget * vbox; GtkWidget * speed_scale, * pitch_scale, * scale_scale; GtkWidget * speed_spin, * pitch_spin, * scale_spin; GtkWidget * speed_hbox, * pitch_hbox, * scale_hbox, * opt_hbox; GtkWidget * speed_frame, * pitch_frame, * scale_frame, * opt_frame; GdkPixmap * logopix; GdkBitmap * logomask; GtkWidget * logo; GtkWidget * logohbox; GtkWidget * logobutton; GtkWidget * volume_toggle; GtkWidget * overlap_toggle; if (sndstretch_config_dialog != NULL) return; sndstretch_config_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_show(sndstretch_config_dialog); /* has to be shown because otherwise ->window would be NULL */ logopix = gdk_pixmap_create_from_xpm_d(sndstretch_config_dialog->window, &logomask, // &sndstretch_config_dialog->style->bg[GTK_STATE_NORMAL], // &style->bg[GTK_STATE_NORMAL], NULL, (gchar **) sndstretch_xmms_logo_xpm); logo = gtk_pixmap_new(logopix,logomask); logobutton = gtk_button_new(); gtk_button_set_relief(GTK_BUTTON(logobutton), GTK_RELIEF_NONE); gtk_container_add(GTK_CONTAINER(logobutton), logo); gtk_signal_connect(GTK_OBJECT(logobutton), "clicked", GTK_SIGNAL_FUNC(sndstretch_config_logobutton_cb), NULL); GTK_WIDGET_SET_FLAGS(logobutton, GTK_CAN_DEFAULT); gtk_widget_grab_default(logobutton); logohbox=gtk_hbox_new(FALSE,0); // to make it rightbound gtk_box_pack_end(GTK_BOX(logohbox), logobutton, FALSE, TRUE, 4); SS.speed_adj = gtk_adjustment_new( 100.0*log(SS.speed)/log(2.0), -100, 100+10, 2, 10, 10); SS.pitch_adj = gtk_adjustment_new( 120.0*log(SS.pitch)/log(2.0), -120, 120+10, 2, 10, 10); SS.scale_adj = gtk_adjustment_new( 100.0*log(SS.scale)/log(2.0), -100, 100+10, 2, 10, 10); volume_toggle = gtk_check_button_new_with_label("Volume corr."); overlap_toggle = gtk_check_button_new_with_label("Short Overlap"); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(volume_toggle), SS.volume_corr ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(overlap_toggle), SS.short_overlap ); gtk_signal_connect(GTK_OBJECT(SS.speed_adj), "value-changed", GTK_SIGNAL_FUNC(speed_change_cb), NULL); gtk_signal_connect(GTK_OBJECT(SS.pitch_adj), "value-changed", GTK_SIGNAL_FUNC(pitch_change_cb), NULL); gtk_signal_connect(GTK_OBJECT(SS.scale_adj), "value-changed", GTK_SIGNAL_FUNC(scale_change_cb), NULL); gtk_signal_connect(GTK_OBJECT(volume_toggle), "toggled", GTK_SIGNAL_FUNC(volume_toggle_cb), NULL); gtk_signal_connect(GTK_OBJECT(overlap_toggle), "toggled", GTK_SIGNAL_FUNC(overlap_toggle_cb), NULL); speed_scale = gtk_hscale_new(GTK_ADJUSTMENT(SS.speed_adj)); pitch_scale = gtk_hscale_new(GTK_ADJUSTMENT(SS.pitch_adj)); scale_scale = gtk_hscale_new(GTK_ADJUSTMENT(SS.scale_adj)); gtk_scale_set_draw_value (GTK_SCALE(speed_scale),FALSE); gtk_scale_set_draw_value (GTK_SCALE(pitch_scale),FALSE); gtk_scale_set_draw_value (GTK_SCALE(scale_scale),FALSE); speed_spin = gtk_spin_button_new(GTK_ADJUSTMENT(SS.speed_adj),1.0,2); pitch_spin = gtk_spin_button_new(GTK_ADJUSTMENT(SS.pitch_adj),1.0,2); scale_spin = gtk_spin_button_new(GTK_ADJUSTMENT(SS.scale_adj),1.0,2); gtk_widget_set_usize (speed_spin,70,20); gtk_widget_set_usize (pitch_spin,70,20); gtk_widget_set_usize (scale_spin,70,20); gtk_entry_set_max_length (GTK_ENTRY(pitch_spin),7); gtk_entry_set_max_length (GTK_ENTRY(speed_spin),7); gtk_entry_set_max_length (GTK_ENTRY(scale_spin),7); speed_hbox = gtk_hbox_new(FALSE,5); pitch_hbox = gtk_hbox_new(FALSE,5); scale_hbox = gtk_hbox_new(FALSE,5); opt_hbox = gtk_hbox_new(FALSE,5); gtk_container_set_border_width(GTK_CONTAINER(speed_hbox), 3); gtk_container_set_border_width(GTK_CONTAINER(pitch_hbox), 3); gtk_container_set_border_width(GTK_CONTAINER(scale_hbox), 3); gtk_container_set_border_width(GTK_CONTAINER(opt_hbox), 3); gtk_box_pack_start(GTK_BOX(speed_hbox), speed_spin, FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(speed_hbox), speed_scale, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(pitch_hbox), pitch_spin, FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(pitch_hbox), pitch_scale, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(scale_hbox), scale_spin, FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(scale_hbox), scale_scale, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(opt_hbox), volume_toggle, FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(opt_hbox), overlap_toggle,TRUE, TRUE, 5); speed_frame = gtk_frame_new("Speed"); pitch_frame = gtk_frame_new("Pitch"); scale_frame = gtk_frame_new("Scale"); opt_frame = gtk_frame_new("Options"); gtk_container_add(GTK_CONTAINER(speed_frame), speed_hbox); gtk_container_add(GTK_CONTAINER(pitch_frame), pitch_hbox); gtk_container_add(GTK_CONTAINER(scale_frame), scale_hbox); gtk_container_add(GTK_CONTAINER(opt_frame), opt_hbox); gtk_container_set_border_width(GTK_CONTAINER(speed_frame), 5); gtk_container_set_border_width(GTK_CONTAINER(pitch_frame), 5); gtk_container_set_border_width(GTK_CONTAINER(scale_frame), 5); gtk_container_set_border_width(GTK_CONTAINER(opt_frame), 5); vbox=gtk_vbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox), pitch_frame, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), speed_frame, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), scale_frame, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), opt_frame, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), logohbox, FALSE, TRUE, 0); gtk_signal_connect(GTK_OBJECT(sndstretch_config_dialog), "destroy", GTK_SIGNAL_FUNC(sndstretch_config_destroy_cb), NULL); gtk_window_set_title(GTK_WINDOW(sndstretch_config_dialog), "SndStretch - configuartion"); gtk_container_add(GTK_CONTAINER(sndstretch_config_dialog), vbox); // gtk_widget_set_usize (sndstretch_config_dialog,275,116); // gtk_widget_set_usize (sndstretch_config_dialog,275,192); gtk_widget_set_usize (sndstretch_config_dialog,275,-1); // gtk_widget_realize(sndstretch_config_dialog); // gdk_window_set_decorations(sndstretch_config_dialog->window, 0); gtk_widget_show_all(sndstretch_config_dialog); } void init_audio( int *aud_handle, // io handle int *fragsize, // fragment size int sampfreq, // sample frequency int stereo, // 0=mono, 1=stereo int format, // sample format const char *device, // read write device int read_access, // read or write int fraglog, // log2(fragsize) int fragnr // # of frags in buffer ) // initialize the soundcard { int audio_io; int blksize; int a_sampsize; int a_stereo; int a_speed; // int mixer_i,mixer_o; // int vol; audio_buf_info info; int fmts; audio_io = open(device, read_access?O_RDONLY:O_WRONLY, 0); #ifdef DEBUG fprintf(stderr,"********************* BEGIN *********************\n"); fprintf(stderr,"Opened device:%s\n", device); #endif #ifdef DEBUG {int caps; ioctl( audio_io, SNDCTL_DSP_GETCAPS, &caps ); fprintf(stderr,"OSS-Version %d\n",caps & DSP_CAP_REVISION); fprintf(stderr," DUPLEX = %X\n",caps & DSP_CAP_DUPLEX ); fprintf(stderr," REALTIME = %X\n",caps & DSP_CAP_REALTIME ); fprintf(stderr," BATCH = %X\n",caps & DSP_CAP_BATCH ); fprintf(stderr," COPROC = %X\n",caps & DSP_CAP_COPROC ); fprintf(stderr," TRIGGER = %X\n",caps & DSP_CAP_TRIGGER ); fprintf(stderr," MMAP = %X\n",caps & DSP_CAP_MMAP ); } #endif // blksize=0x00030006; // ( 2^4 = 16 bytes ) * ( 3 fragments ) // ioctl(audio_i, SNDCTL_DSP_SETFRAGMENT, &blksize); // blksize=0x0008000A; // 2^4 = 16 // blksize=0x00080004; // 2^4 = 16 blksize = (fragnr<<16) + fraglog; // fprintf(stderr," blksize = %0X\n",blksize ); // blksize=0x00000004; // 4 * 2^4 = 64 byte ioctl(audio_io, SNDCTL_DSP_SETFRAGMENT, &blksize); //ioctl(audio, SNDCTL_DSP_SETBLKSIZE, &blksize); ioctl(audio_io, SNDCTL_DSP_GETOSPACE, &info); #ifdef DEBUG fprintf(stderr,"Info for output\n"); fprintf(stderr," fragments = %d\n",info.fragments ); fprintf(stderr," fragstotal = %d\n",info.fragstotal); fprintf(stderr," fragsize = %d\n",info.fragsize ); fprintf(stderr," bytes = %d\n",info.bytes ); #endif *fragsize=info.fragsize; ioctl(audio_io, SNDCTL_DSP_GETFMTS, &fmts); #ifdef DEBUG fprintf(stderr,"Formats supported (output):\n"); fprintf(stderr," AFMT_QUERY = %d\n", (AFMT_QUERY & fmts)!=0); fprintf(stderr," AFMT_MU_LAW = %d\n", (AFMT_MU_LAW & fmts)!=0); fprintf(stderr," AFMT_A_LAW = %d\n", (AFMT_A_LAW & fmts)!=0); fprintf(stderr," AFMT_IMA_ADPCM = %d\n", (AFMT_IMA_ADPCM & fmts)!=0); fprintf(stderr," AFMT_U8 = %d\n", (AFMT_U8 & fmts)!=0); fprintf(stderr," AFMT_S16_LE = %d\n", (AFMT_S16_LE & fmts)!=0); fprintf(stderr," AFMT_S8 = %d\n", (AFMT_S8 & fmts)!=0); fprintf(stderr," AFMT_U16_LE = %d\n", (AFMT_U16_LE & fmts)!=0); fprintf(stderr," AFMT_U16_BE = %d\n", (AFMT_U16_BE & fmts)!=0); fprintf(stderr," AFMT_MPEG = %d\n", (AFMT_MPEG & fmts)!=0); #endif if( (fmts & format) == 0 ){ fprintf(stderr,"!!!! Audioformat not supported by driver/hardware !!!!\n"); } ioctl(audio_io, SOUND_PCM_READ_CHANNELS, &blksize); #ifdef DEBUG fprintf(stderr,"channels=%d\n",blksize); #endif // ioctl(audio_i, SNDCTL_DSP_SYNC, NULL); // ioctl(audio_o, SNDCTL_DSP_SYNC, NULL); // a_sampsize = AFMT_S16_LE; a_sampsize = format; ioctl(audio_io, SNDCTL_DSP_SAMPLESIZE, &a_sampsize); a_stereo = stereo; ioctl(audio_io, SNDCTL_DSP_STEREO, &a_stereo); a_speed = sampfreq; #ifdef DEBUG fprintf(stderr,"setting to: %d\n",a_speed); #endif ioctl(audio_io, SNDCTL_DSP_SPEED, &a_speed); #ifdef DEBUG fprintf(stderr,"sampfreq after SNDCTL_DSP_SPEED = %d\n",a_speed); #endif ioctl(audio_io, SOUND_PCM_READ_RATE, &a_speed); #ifdef DEBUG fprintf(stderr,"SOUND_PCM_READ_RATE (sampfreq got) = %d\n",a_speed); #endif #ifdef DEBUG fprintf(stderr,"********************** END **********************\n"); #endif //sampfreq_exact=sampfreq; // mixer_o = open("/dev/mixer", O_RDONLY, 0); // vol=0xFFFF; // ioctl(mixer_o, SOUND_MIXER_WRITE_VOLUME, &vol); // mixer_i = open("/dev/mixer1", O_RDONLY, 0); // vol=0xFFFF; // ioctl(mixer_i, SOUND_MIXER_WRITE_PCM, &vol); //printf("MIC-Volume=%d\n",vol); //close(mixer); // printf("Mixer set\n"); *aud_handle=audio_io; } void sndstretch_init(void) { ConfigFile *cfgfile; SS.fragsize=0; SS.chnr=2; SS.paused=0; SS.time_offs=0; SS.fmtsize=2; SS.fmt=FMT_S16_NE; SS.sampfreq=44100; SS.written=0; SS.bpsec=176400; SS.vol_r=50; SS.vol_l=50; SS.pitch=1.0; SS.speed=1.0; SS.scale=1.0; if ( (cfgfile = xmms_cfg_open_default_file()) ) { gboolean b; xmms_cfg_read_double( cfgfile, "sndstretch", "pitch", &SS.pitch ); xmms_cfg_read_double( cfgfile, "sndstretch", "speed", &SS.speed ); if ( xmms_cfg_read_boolean( cfgfile, "sndstretch", "short_overlap", &b ) ) SS.short_overlap = b; if ( xmms_cfg_read_boolean( cfgfile, "sndstretch", "volume_corr", &b ) ) SS.volume_corr = b; xmms_cfg_free( cfgfile ); } // sndstretch_config(); } void sndstretch_get_volume (int *l, int *r) { int mixer_o,vol; mixer_o = open("/dev/mixer", O_RDONLY, 0); ioctl(mixer_o, SOUND_MIXER_READ_PCM, &vol); *l = (vol & 0xFF); *r = ((vol>>8) & 0xFF); // *l = (vol & 0xFF) *100/255; // *r = ((vol>>8) & 0xFF) *100/255; close(mixer_o); // *l=SS.vol_l; // *r=SS.vol_r; } void sndstretch_set_volume (int l, int r) /* Set the volume */ { int mixer_o,vol; int ll,rr; // ll=l*0xFF/100; // rr=r*0xFF/100; ll=l; rr=r; mixer_o = open("/dev/mixer", O_WRONLY, 0); vol=(ll & 0xFF)+((rr & 0xFF)<<8); ioctl(mixer_o, SOUND_MIXER_WRITE_PCM, &vol); close(mixer_o); SS.vol_l=l; SS.vol_r=r; } int sndstretch_open_audio (AFormat fmt, int rate, int nch) /* Open the device, if the device can't handle the given parameters the plugin is responsible for downmixing the data to the right format before outputting it */ { int handle,fragsize,format; // fprintf(stderr,"sndstretch_open_audio() called\n"); format=AFMT_S16_BE; switch (fmt) { case FMT_U8 : format = AFMT_U8; SS.fmtsize=1; break; case FMT_S8 : format = AFMT_S8; SS.fmtsize=1; break; case FMT_U16_LE : format = AFMT_U16_LE; SS.fmtsize=2; break; case FMT_U16_BE : format = AFMT_U16_BE; SS.fmtsize=2; break; case FMT_U16_NE : format = AFMT_U16_LE; SS.fmtsize=2; break; case FMT_S16_LE : format = AFMT_S16_LE; SS.fmtsize=2; break; case FMT_S16_BE : format = AFMT_S16_BE; SS.fmtsize=2; break; case FMT_S16_NE : format = AFMT_S16_LE; SS.fmtsize=2; break; } init_audio( &handle, // io handle &fragsize, // fragment size rate, // sample frequency nch-1, // 0=mono, 1=stereo format, // sample format "/dev/dsp", // read write device 0, // 0=write 11, // log2(fragsize) 10 // # of frags in buffer ); SS.handle=handle; SS.fmt=fmt; SS.sampfreq=rate; SS.chnr=nch; SS.bpsec=SS.fmtsize*SS.chnr*rate; SS.written=0; SS.time_offs=0; SS.going=TRUE; return handle; } void sndstretch_write_audio (void *ptr, int length) /* The input plugin calls this to write data to the output buffer */ { static short int buff_o[65536]; static int prod_size; static PitchSpeedJob job; static int init_job=1; // EffectPlugin *ep; // fprintf(stderr,"sndstretch_write_audio(ptr,%d) called\n",length); if(SS.going){ /* ep = get_current_effect_plugin(); if(effects_enabled() && ep && ep->mod_samples){ length = ep->mod_samples( ptr, length, SS.fmt, SS.sampfreq, SS.chnr); }*/ if(init_job){ InitPitchSpeedJob(&job); init_job=0; } snd_pitch_speed_job( ptr, SS.chnr, length/SS.fmtsize, 0, SS.pitch, SS.speed, /*882*/1764, buff_o, &prod_size, &job, SS.volume_corr ); // snd_stretch_scale( ptr, (s16 *)buff_o, SS.pitch, SS.speed, SS.chnr, // length/SS.fmtsize, &prod_size, 0 ); write(SS.handle,buff_o,prod_size*SS.fmtsize); // write(SS.handle,ptr,length); SS.written+=length; if( SS.written > SS.bpsec*5 ){ SS.written-=SS.bpsec*5; SS.time_offs+=5000; } } } void sndstretch_close_audio (void) { SS.going=FALSE; SS.written=0; SS.time_offs=0; // fprintf(stderr,"sndstretch_close_audio() called\n"); close(SS.handle); } void sndstretch_flush (int time) /* Flush the buffer and set the plugins internal timers to time */ { // flush( SS.handle ); // fprintf(stderr,"sndstretch_flush(%d) called\n",time); SS.written=0; SS.time_offs=time; } void sndstretch_pause (short paused) /* Pause or unpause the output */ { SS.paused=paused; } int sndstretch_buffer_free (void) /* Return the amount of data that can be written to the buffer, two calls to this without a call to write_audio should make the plugin output audio directly */ { /* audio_buf_info info; ioctl(SS.handle, SNDCTL_DSP_GETOSPACE, &info); return(info.fragments*info.fragsize*SS.speed-1);*/ if(SS.paused){ return(0); }else{ return(8192); } } int sndstretch_buffer_playing (void) /* Returns TRUE if the plugin currently is playing some audio, otherwise return FALSE */ { // fprintf(stderr,"sndstretch_buffer_playing() called\n"); // return(!(SS.paused)); // return(TRUE); // 24/10/01 MLS The method previously returned TRUE all the time, this meant whenever XMMS checked the plugin, it was saying it 'was' playing sound. XMMS only checks at the end of a song when we've reported that we've processed the entire buffer. So return FALSE, we're now not playing music. // thanks to Michael Lucas-Smith return(FALSE); /* hope this does nothing unforeseen */ } int sndstretch_output_time (void) /* Return the current playing time */ { // fprintf(stderr,"sndstretch_output_time() called - "); // fprintf(stderr,"%d returned\n",SS.time_offs + (gint) ((SS.written * 1000) / SS.bpsec )); // fprintf(stderr,"written = %d\n",SS.written); return( SS.time_offs + (gint) ((SS.written * 1000) / SS.bpsec ) ); } int sndstretch_written_time (void) /* Return the length of all the data that has been written to buffer */ { // fprintf(stderr,"sndstretch_output_time() called - "); // fprintf(stderr,"%d returned\n",SS.time_offs + (gint) ((SS.written * 1000) / SS.bpsec )); return( SS.time_offs + (gint) ((SS.written * 1000) / SS.bpsec ) ); } int sndstretch_mod_samples (gpointer *ptr, gint length, AFormat fmt, gint srate, gint nch) /* Modify samples */ /* The input plugin calls this to write data to the output buffer */ { static short int * buff_o; static int prod_size; static PitchSpeedJob job; static int init_job=1; buff_o=realloc( buff_o, 65536 ); if(init_job){ InitPitchSpeedJob(&job); init_job=0; } snd_pitch_speed_job( *ptr, nch, length/2/*SS.fmtsize*/, 0, SS.pitch, SS.speed, (SS.short_overlap) ? 882 : 1764 , buff_o, &prod_size, &job, SS.volume_corr ); *ptr = (gpointer) buff_o; return prod_size*2; }