Mercurial > audlegacy-plugins
view src/wav/wav-sndfile.c @ 1691:aa40f1404257
Automated merge with file:/home/ccr/audacious/plugins
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 07 Sep 2007 12:46:01 +0300 |
parents | d158ce84fda7 cf7124d464f9 |
children | f35f9d6fcb6d |
line wrap: on
line source
/* Audacious - Cross-platform multimedia player * Copyright (C) 2005 Audacious development team. * * Based on the xmms_sndfile input plugin: * Copyright (C) 2000, 2002 Erik de Castro Lopo * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * Rewritten 17-Feb-2007 (nenolod): * - now uses conditional variables to ensure that sndfile mutex is * entirely protected. * - pausing works now * - fixed some potential race conditions when dealing with NFS. * - TITLE_LEN removed */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <glib.h> #include <string.h> #include <math.h> #include "audacious/plugin.h" #include "audacious/util.h" #include "audacious/i18n.h" #include "audacious/main.h" #include "audacious/tuple.h" #include "audacious/tuple_formatter.h" #include "audacious/output.h" #include "wav-sndfile.h" #include <sndfile.h> static SNDFILE *sndfile = NULL; static SF_INFO sfinfo; static int song_length; static int bit_rate = 0; static glong seek_time = -1; static GThread *decode_thread; static GMutex *decode_mutex; static GCond *decode_cond; InputPlugin wav_ip = { .init = plugin_init, .about = wav_about, .is_our_file = is_our_file, .play_file = play_start, .stop = play_stop, .pause = play_pause, .seek = file_seek, .cleanup = plugin_cleanup, .get_song_info = get_song_info, .get_song_tuple = get_song_tuple, .mseek = file_mseek, }; static int get_song_length (char *filename) { SNDFILE *tmp_sndfile; SF_INFO tmp_sfinfo; gchar *realfn = NULL; realfn = g_filename_from_uri(filename, NULL, NULL); tmp_sndfile = sf_open (realfn ? realfn : filename, SFM_READ, &tmp_sfinfo); g_free(realfn); realfn = NULL; if (!tmp_sndfile) { return 0; } sf_close (tmp_sndfile); tmp_sndfile = NULL; if (tmp_sfinfo.samplerate <= 0) return 0; return (int) ceil (1000.0 * tmp_sfinfo.frames / tmp_sfinfo.samplerate); } static void fill_song_tuple (char *filename, Tuple *ti) { SNDFILE *tmp_sndfile; SF_INFO tmp_sfinfo; unsigned int lossy = 0; gchar *realfn = NULL, *codec = NULL, *format, *subformat = NULL; GString *codec_gs = NULL; realfn = g_filename_from_uri(filename, NULL, NULL); tmp_sndfile = sf_open (realfn ? realfn : filename, SFM_READ, &tmp_sfinfo); if ( sf_get_string(tmp_sndfile, SF_STR_TITLE) == NULL) tuple_associate_string(ti, FIELD_TITLE, NULL, g_path_get_basename(realfn ? realfn : filename)); else tuple_associate_string(ti, FIELD_TITLE, NULL, sf_get_string(tmp_sndfile, SF_STR_TITLE)); tuple_associate_string(ti, FIELD_ARTIST, NULL, sf_get_string(tmp_sndfile, SF_STR_ARTIST)); tuple_associate_string(ti, FIELD_COMMENT, NULL, sf_get_string(tmp_sndfile, SF_STR_COMMENT)); tuple_associate_string(ti, -1, "date", sf_get_string(tmp_sndfile, SF_STR_DATE)); tuple_associate_string(ti, -1, "software", sf_get_string(tmp_sndfile, SF_STR_SOFTWARE)); g_free(realfn); realfn = NULL; if (!tmp_sndfile) return; sf_close (tmp_sndfile); tmp_sndfile = NULL; if (tmp_sfinfo.samplerate > 0) tuple_associate_int(ti, FIELD_LENGTH, NULL, (int) ceil (1000.0 * tmp_sfinfo.frames / tmp_sfinfo.samplerate)); switch (tmp_sfinfo.format & SF_FORMAT_TYPEMASK) { case SF_FORMAT_WAV: case SF_FORMAT_WAVEX: format = "Microsoft WAV"; break; case SF_FORMAT_AIFF: format = "Apple/SGI AIFF"; break; case SF_FORMAT_AU: format = "Sun/NeXT AU"; break; case SF_FORMAT_RAW: format = "Raw PCM data"; break; case SF_FORMAT_PAF: format = "Ensoniq PARIS"; break; case SF_FORMAT_SVX: format = "Amiga IFF / SVX8 / SV16"; break; case SF_FORMAT_NIST: format = "Sphere NIST"; break; case SF_FORMAT_VOC: format = "Creative VOC"; break; case SF_FORMAT_IRCAM: format = "Berkeley/IRCAM/CARL"; break; case SF_FORMAT_W64: format = "Sonic Foundry's 64 bit RIFF/WAV"; break; case SF_FORMAT_MAT4: format = "Matlab (tm) V4.2 / GNU Octave 2.0"; break; case SF_FORMAT_MAT5: format = "Matlab (tm) V5.0 / GNU Octave 2.1"; break; case SF_FORMAT_PVF: format = "Portable Voice Format"; break; case SF_FORMAT_XI: format = "Fasttracker 2 Extended Instrument"; break; case SF_FORMAT_HTK: format = "HMM Tool Kit"; break; case SF_FORMAT_SDS: format = "Midi Sample Dump Standard"; break; case SF_FORMAT_AVR: format = "Audio Visual Research"; break; case SF_FORMAT_SD2: format = "Sound Designer 2"; break; case SF_FORMAT_FLAC: format = "Free Lossless Audio Codec"; break; case SF_FORMAT_CAF: format = "Core Audio File"; break; default: format = "unknown sndfile"; } switch (tmp_sfinfo.format & SF_FORMAT_SUBMASK) { case SF_FORMAT_PCM_S8: subformat = "signed 8 bit"; break; case SF_FORMAT_PCM_16: subformat = "signed 16 bit"; break; case SF_FORMAT_PCM_24: subformat = "signed 24 bit"; break; case SF_FORMAT_PCM_32: subformat = "signed 32 bit"; break; case SF_FORMAT_PCM_U8: subformat = "unsigned 8 bit"; break; case SF_FORMAT_FLOAT: subformat = "32 bit float"; break; case SF_FORMAT_DOUBLE: subformat = "64 bit float"; break; case SF_FORMAT_ULAW: subformat = "U-Law"; lossy = 1; break; case SF_FORMAT_ALAW: subformat = "A-Law"; lossy = 1; break; case SF_FORMAT_IMA_ADPCM: subformat = "IMA ADPCM"; lossy = 1; break; case SF_FORMAT_MS_ADPCM: subformat = "MS ADPCM"; lossy = 1; break; case SF_FORMAT_GSM610: subformat = "GSM 6.10"; lossy = 1; break; case SF_FORMAT_VOX_ADPCM: subformat = "Oki Dialogic ADPCM"; lossy = 1; break; case SF_FORMAT_G721_32: subformat = "32kbs G721 ADPCM"; lossy = 1; break; case SF_FORMAT_G723_24: subformat = "24kbs G723 ADPCM"; lossy = 1; break; case SF_FORMAT_G723_40: subformat = "40kbs G723 ADPCM"; lossy = 1; break; case SF_FORMAT_DWVW_12: subformat = "12 bit Delta Width Variable Word"; lossy = 1; break; case SF_FORMAT_DWVW_16: subformat = "16 bit Delta Width Variable Word"; lossy = 1; break; case SF_FORMAT_DWVW_24: subformat = "24 bit Delta Width Variable Word"; lossy = 1; break; case SF_FORMAT_DWVW_N: subformat = "N bit Delta Width Variable Word"; lossy = 1; break; case SF_FORMAT_DPCM_8: subformat = "8 bit differential PCM"; break; case SF_FORMAT_DPCM_16: subformat = "16 bit differential PCM"; } codec_gs = g_string_new(""); if (subformat != NULL) g_string_append_printf(codec_gs, "%s (%s)", format, subformat); else g_string_append_printf(codec_gs, "%s", format); codec = g_strdup(codec_gs->str); g_string_free(codec_gs, TRUE); tuple_associate_string(ti, FIELD_CODEC, NULL, codec); if (lossy != 0) tuple_associate_string(ti, FIELD_QUALITY, NULL, "lossy"); else tuple_associate_string(ti, FIELD_QUALITY, NULL, "lossless"); } static gchar *get_title(char *filename) { Tuple *tuple; gchar *title; tuple = tuple_new_from_filename(filename); fill_song_tuple(filename, tuple); title = tuple_formatter_make_title_string(tuple, get_gentitle_format()); if (*title == '\0') { g_free(title); title = g_strdup(tuple_get_string(tuple, FIELD_FILE_NAME, NULL)); } tuple_free(tuple); return title; } static void plugin_init (void) { seek_time = -1; decode_mutex = g_mutex_new(); decode_cond = g_cond_new(); } static void plugin_cleanup (void) { g_cond_free(decode_cond); g_mutex_free(decode_mutex); } static int is_our_file (char *filename) { SNDFILE *tmp_sndfile; SF_INFO tmp_sfinfo; gchar *realfn = NULL; realfn = g_filename_from_uri(filename, NULL, NULL); /* Have to open the file to see if libsndfile can handle it. */ tmp_sndfile = sf_open (realfn ? realfn : filename, SFM_READ, &tmp_sfinfo); g_free(realfn); realfn = NULL; if (!tmp_sndfile) { return FALSE; } /* It can so close file and return TRUE. */ sf_close (tmp_sndfile); tmp_sndfile = NULL; return TRUE; } static gpointer play_loop (gpointer arg) { static short buffer [BUFFER_SIZE]; int samples; InputPlayback *playback = arg; for (;;) { GTimeVal sleeptime; /* sf_read_short will return 0 for all reads at EOF. */ samples = sf_read_short (sndfile, buffer, BUFFER_SIZE); if (samples > 0 && playback->playing == TRUE) { while ((playback->output->buffer_free () < samples) && playback->playing == TRUE) { g_get_current_time(&sleeptime); g_time_val_add(&sleeptime, 500000); g_mutex_lock(decode_mutex); g_cond_timed_wait(decode_cond, decode_mutex, &sleeptime); g_mutex_unlock(decode_mutex); if (playback->playing == FALSE) break; } produce_audio (playback->output->written_time (), FMT_S16_NE, sfinfo.channels, samples * sizeof (short), buffer, &playback->playing); } else { while(playback->output->buffer_playing()) { g_get_current_time(&sleeptime); g_time_val_add(&sleeptime, 500000); g_mutex_lock(decode_mutex); g_cond_timed_wait(decode_cond, decode_mutex, &sleeptime); g_mutex_unlock(decode_mutex); if(playback->playing == FALSE) break; } playback->eof = TRUE; playback->playing = FALSE; g_mutex_unlock(decode_mutex); break; } /* Do seek if seek_time is valid. */ if (seek_time >= 0) { sf_seek (sndfile, (sf_count_t)((gint64)seek_time * (gint64)sfinfo.samplerate / 1000L), SEEK_SET); playback->output->flush (seek_time); seek_time = -1; } if (playback->playing == FALSE) break; } sf_close (sndfile); sndfile = NULL; seek_time = -1; playback->output->close_audio(); return NULL; } static void play_start (InputPlayback *playback) { gchar *realfn = NULL; int pcmbitwidth; gchar *song_title; if (sndfile) /* already opened */ return; pcmbitwidth = 32; song_title = get_title(playback->filename); realfn = g_filename_from_uri(playback->filename, NULL, NULL); sndfile = sf_open (realfn ? realfn : playback->filename, SFM_READ, &sfinfo); g_free(realfn); realfn = NULL; if (!sndfile) return; bit_rate = sfinfo.samplerate * pcmbitwidth; if (sfinfo.samplerate > 0) song_length = (int) ceil (1000.0 * sfinfo.frames / sfinfo.samplerate); else song_length = 0; if (! playback->output->open_audio (FMT_S16_NE, sfinfo.samplerate, sfinfo.channels)) { sf_close (sndfile); sndfile = NULL; return; } wav_ip.set_info (song_title, song_length, bit_rate, sfinfo.samplerate, sfinfo.channels); g_free (song_title); playback->playing = TRUE; decode_thread = g_thread_self(); playback->set_pb_ready(playback); play_loop(playback); } static void play_pause (InputPlayback *playback, gshort p) { playback->output->pause(p); } static void play_stop (InputPlayback *playback) { if (decode_thread == NULL) return; g_mutex_lock(decode_mutex); playback->playing = FALSE; g_mutex_unlock(decode_mutex); g_cond_signal(decode_cond); g_thread_join (decode_thread); sndfile = NULL; decode_thread = NULL; seek_time = -1; } static void file_mseek (InputPlayback *playback, gulong millisecond) { if (! sfinfo.seekable) return; seek_time = (glong)millisecond; while (seek_time != -1) xmms_usleep (80000); } static void file_seek (InputPlayback *playback, int time) { gulong millisecond = time * 1000; file_mseek(playback, millisecond); } static void get_song_info (char *filename, char **title, int *length) { (*length) = get_song_length(filename); (*title) = get_title(filename); } static Tuple* get_song_tuple (gchar *filename) { Tuple *ti = tuple_new_from_filename(filename); fill_song_tuple(filename, ti); return ti; } static void wav_about(void) { static GtkWidget *box; if (!box) { box = xmms_show_message( _("About sndfile WAV support"), _("Adapted for Audacious usage by Tony Vroon <chainsaw@gentoo.org>\n" "from the xmms_sndfile plugin which is:\n" "Copyright (C) 2000, 2002 Erik de Castro Lopo\n\n" "This program is free software ; you can redistribute it and/or modify \n" "it under the terms of the GNU General Public License as published by \n" "the Free Software Foundation ; either version 2 of the License, or \n" "(at your option) any later version. \n \n" "This program is distributed in the hope that it will be useful, \n" "but WITHOUT ANY WARRANTY ; without even the implied warranty of \n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. \n" "See the GNU General Public License for more details. \n\n" "You should have received a copy of the GNU General Public \n" "License along with this program ; if not, write to \n" "the Free Software Foundation, Inc., \n" "51 Franklin Street, Fifth Floor, \n" "Boston, MA 02110-1301 USA"), _("Ok"), FALSE, NULL, NULL); g_signal_connect(G_OBJECT(box), "destroy", (GCallback)gtk_widget_destroyed, &box); } } void init(void) { wav_ip.description = g_strdup_printf(_("sndfile WAV plugin")); } void fini(void) { g_free(wav_ip.description); } InputPlugin *wav_iplist[] = { &wav_ip, NULL }; DECLARE_PLUGIN(wav-sndfile, init, fini, wav_iplist, NULL, NULL, NULL, NULL, NULL)