/* Audio File Library plug-in for XMMS Copyright (c) 2002, Michael Pruett 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 #include #include #include #include #include #include static void about (void); static int get_time (void); static int is_our_file (char *filename); static void stop (void); static void play_file (char *filename); static void *audiofile_play_loop (void *arg); static void pause (short paused); static void get_song_info (char *filename, char **title, int *length); static void seek (int time); InputPlugin audiofile_ip = { NULL, NULL, NULL, NULL, about, NULL, is_our_file, NULL, play_file, stop, pause, seek, NULL, get_time, NULL, NULL, NULL, NULL, NULL, NULL, NULL, get_song_info, NULL, NULL }; #define BUFFER_FRAME_COUNT 512 static pthread_t thread_id; static gboolean playing = FALSE; static int seek_to_time = -1; InputPlugin *get_iplugin_info (void) { audiofile_ip.description = g_strdup_printf("Audio File Library Plug-in %s", VERSION); return &audiofile_ip; } static void about (void) { static GtkWidget *box; box = xmms_show_message( "About Audio File Library Plug-in", "Audio File Library Plug-in by Michael Pruett \n" "This plug-in plays many common file formats, including\n" "AIFF, AIFF-C, WAVE, and NeXT/Sun .snd/.au files.", "Ok", FALSE, NULL, NULL); gtk_signal_connect(GTK_OBJECT(box), "destroy", gtk_widget_destroyed, &box); } static int is_our_file (char *filename) { AFfilehandle file; afSetErrorHandler(NULL); file = afOpenFile(filename, "r", NULL); if (file == AF_NULL_FILEHANDLE) return FALSE; afCloseFile(file); return TRUE; } static void *audiofile_play_loop (void *arg) { AFfilehandle file = (AFfilehandle) arg; void *data; int framesize; double rate; rate = afGetRate(file, AF_DEFAULT_TRACK); /* Read the audio data as 16-bit two's complement samples. */ afSetVirtualSampleFormat(file, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16); framesize = afGetVirtualFrameSize(file, AF_DEFAULT_TRACK, TRUE); data = g_malloc(framesize * BUFFER_FRAME_COUNT); if (data == NULL) { audiofile_ip.set_info_text("Couldn't allocate audio buffer"); playing = FALSE; pthread_exit(NULL); } while (playing) { AFframecount framecount; if (seek_to_time >= 0) { afSeekFrame(file, AF_DEFAULT_TRACK, (AFframecount) (seek_to_time * rate)); audiofile_ip.output->flush(seek_to_time * 1000); seek_to_time = -1; } framecount = afReadFrames(file, AF_DEFAULT_TRACK, data, BUFFER_FRAME_COUNT); if (framecount < 0) { audiofile_ip.output->buffer_free(); playing = FALSE; } audiofile_ip.add_vis_pcm(audiofile_ip.output->written_time(), FMT_S16_NE, 1, framecount * framesize, data); /* If the buffer doesn't have enough space, sleep for 10 ms. */ while (playing && (audiofile_ip.output->buffer_free() < framecount * framesize)) { xmms_usleep(10000); } if (playing) audiofile_ip.output->write_audio(data, framecount * framesize); } afCloseFile(file); audiofile_ip.output->buffer_free(); g_free(data); pthread_exit(NULL); } static void play_file (char *filename) { AFfilehandle file; double rate; int channels; file = afOpenFile(filename, "r", NULL); if (file == AF_NULL_FILEHANDLE) { audiofile_ip.set_info_text("Couldn't open audio file"); return; } channels = afGetChannels(file, AF_DEFAULT_TRACK); rate = afGetRate(file, AF_DEFAULT_TRACK); if (audiofile_ip.output->open_audio(FMT_S16_NE, (int) rate, channels) == 0) { audiofile_ip.set_info_text("Couldn't open audio output"); return; } /* We insist on 16-bit samples, so one sample frame is channels * 16 bits wide. Therefore the audio is output at a rate of 16 * channels * rate bits per second. */ audiofile_ip.set_info(filename, 1000 * afGetFrameCount(file, AF_DEFAULT_TRACK) / rate, 16 * channels * rate, rate, channels); playing = TRUE; pthread_create(&thread_id, NULL, audiofile_play_loop, file); } static void stop (void) { if (playing) { playing = FALSE; pthread_join(thread_id, NULL); audiofile_ip.output->close_audio(); } } static void pause (short paused) { audiofile_ip.output->pause(paused); } static void get_song_info (char *filename, char **title, int *length) { AFfilehandle file; double rate; AFframecount frames; file = afOpenFile(filename, "r", NULL); if (file == AF_NULL_FILEHANDLE) return; strcpy(*title, filename); rate = afGetRate(file, AF_DEFAULT_TRACK); frames = afGetFrameCount(file, AF_DEFAULT_TRACK); *length = 1000.0 * ((double) frames) / rate; afCloseFile(file); } /* time is given in seconds and specifies an absolute time since the beginning of the track. */ static void seek (int time) { seek_to_time = time; while (seek_to_time != -1) xmms_usleep(10000); } static int get_time (void) { if (playing == FALSE) return -1; if (!audiofile_ip.output->buffer_playing()) return -1; else { return audiofile_ip.output->output_time(); } }