📄 vorbis.c
字号:
/* * Copyright (C) Tony Arcieri <bascule@inferno.tusculum.edu> * Copyright (C) 2001-2002 Haavard Kvaalen <havardk@xmms.org> * * ReplayGain processing Copyright (C) 2002 Gian-Carlo Pascutto <gcp@sjeng.org> * * 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. * *//* * 2002-01-11 ReplayGain processing added by Gian-Carlo Pascutto <gcp@sjeng.org> *//* * Note that this uses vorbisfile, which is not (currently) * thread-safe. */#include "config.h"#include <stdio.h>#include <fcntl.h>#include <stdlib.h>#include <math.h>#include <string.h>#include <pthread.h>#include <glib.h>#include <gtk/gtk.h>#include <ogg/ogg.h>#include <vorbis/codec.h>#include <vorbis/vorbisfile.h>#include "xmms/plugin.h"#include "libxmms/util.h"#include "libxmms/configfile.h"#include "libxmms/titlestring.h"#include <xmms/i18n.h>#include "vorbis.h"#include "http.h"extern vorbis_config_t vorbis_cfg;static int vorbis_check_file(char *filename);static void vorbis_play(char *filename);static void vorbis_stop(void);static void vorbis_pause(short p);static void vorbis_seek(int time);static int vorbis_time(void);static void vorbis_get_song_info(char *filename, char **title, int *length);static gchar *vorbis_generate_title(OggVorbis_File *vorbisfile, char *fn);static void vorbis_aboutbox(void);static void vorbis_init(void);static long vorbis_process_replaygain(float **pcm, int samples, int ch, char *pcmout, float rg_scale);static gboolean vorbis_update_replaygain(float *scale);static size_t ovcb_read(void *ptr, size_t size, size_t nmemb, void *datasource);static int ovcb_seek(void *datasource, int64_t offset, int whence);static int ovcb_close(void *datasource);static long ovcb_tell(void *datasource);ov_callbacks vorbis_callbacks = { ovcb_read, ovcb_seek, ovcb_close, ovcb_tell};InputPlugin vorbis_ip ={ NULL, NULL, NULL, /* description */ vorbis_init, /* init */ vorbis_aboutbox, /* aboutbox */ vorbis_configure, /* configure */ vorbis_check_file, /* is_our_file */ NULL, vorbis_play, vorbis_stop, vorbis_pause, vorbis_seek, NULL, /* set eq */ vorbis_time, NULL, NULL, NULL, NULL, NULL, NULL, NULL, vorbis_get_song_info, vorbis_file_info_box, /* file info box, tag editing */ NULL};static OggVorbis_File vf;static pthread_t tid;int vorbis_playing = 0;static int vorbis_eos = 0;static int vorbis_is_streaming = 0;static int vorbis_bytes_streamed = 0;static volatile int seekneeded = -1;static int samplerate, channels;pthread_mutex_t vf_mutex = PTHREAD_MUTEX_INITIALIZER;static gboolean output_error; InputPlugin *get_iplugin_info(void){ vorbis_ip.description = g_strdup_printf(_("Ogg Vorbis Player %s"), VERSION); return &vorbis_ip;}static int vorbis_check_file(char *filename){ FILE *stream; OggVorbis_File vfile; /* avoid thread interaction */ char *ext; /* is this our http resource? */ if (strncasecmp(filename, "http://", 7) == 0) { ext = strrchr(filename, '.'); if (ext) { if (!strncasecmp(ext, ".ogg", 4)) { return TRUE; } } return FALSE; } if ((stream = fopen(filename, "r")) == NULL) return FALSE; /* * The open function performs full stream detection and machine * initialization. If it returns zero, the stream *is* Vorbis and * we're fully ready to decode. */ /* libvorbisfile isn't thread safe... */ memset(&vfile, 0, sizeof(vfile)); pthread_mutex_lock(&vf_mutex); if (ov_open(stream, &vfile, NULL, 0) < 0) { pthread_mutex_unlock(&vf_mutex); fclose(stream); return FALSE; } ov_clear(&vfile); /* once the ov_open succeeds, the stream belongs to vorbisfile.a. ov_clear will fclose it */ pthread_mutex_unlock(&vf_mutex); return TRUE;}static void vorbis_jump_to_time(long time){ pthread_mutex_lock(&vf_mutex); /* * We need to guard against seeking to the end, or things * don't work right. Instead, just seek to one second prior * to this */ if (time == ov_time_total(&vf, -1)) time--; vorbis_ip.output->flush(time * 1000); ov_time_seek(&vf, time); pthread_mutex_unlock(&vf_mutex);}#ifdef WORDS_BIGENDIAN#define XMMS_BIG_ENDIAN TRUE#define GET_BYTE1(val) ((val) >> 8)#define GET_BYTE2(val) ((val) & 0xff)#else#define XMMS_BIG_ENDIAN FALSE#define GET_BYTE1(val) ((val) & 0xff)#define GET_BYTE2(val) ((val) >> 8)#endifstatic void do_seek(void){ if (seekneeded != -1 && !vorbis_is_streaming) { vorbis_jump_to_time(seekneeded); seekneeded = -1; vorbis_eos = FALSE; }}static int vorbis_process_data(int last_section, gboolean use_rg, float rg_scale){ char pcmout[4096]; const int bep = XMMS_BIG_ENDIAN; int bytes; float **pcm; /* * A vorbis physical bitstream may consist of many logical * sections (information for each of which may be fetched from * the vf structure). This value is filled in by ov_read to * alert us what section we're currently decoding in case we * need to change playback settings at a section boundary */ int current_section; pthread_mutex_lock(&vf_mutex); if (use_rg) { bytes = ov_read_float(&vf, &pcm, sizeof(pcmout)/2/channels, ¤t_section); if (bytes > 0) bytes = vorbis_process_replaygain(pcm, bytes, channels, pcmout, rg_scale); } else bytes = ov_read(&vf, pcmout, sizeof(pcmout), bep, 2, 1, ¤t_section); switch (bytes) { case 0: /* EOF */ pthread_mutex_unlock(&vf_mutex); vorbis_ip.output->buffer_free(); vorbis_ip.output->buffer_free(); vorbis_eos = TRUE; return last_section; case OV_HOLE: case OV_EBADLINK: /* * error in the stream. Not a problem, just * reporting it in case we (the app) cares. * In this case, we don't. */ pthread_mutex_unlock(&vf_mutex); return last_section; } if (current_section != last_section) { /* * The info struct is different in each section. vf * holds them all for the given bitstream. This * requests the current one */ vorbis_info* vi = ov_info(&vf, -1); if (vi->channels > 2) { vorbis_eos = TRUE; pthread_mutex_unlock(&vf_mutex); return current_section; } if (vi->rate != samplerate || vi->channels != channels) { samplerate = vi->rate; channels = vi->channels; vorbis_ip.output->buffer_free(); vorbis_ip.output->buffer_free(); vorbis_ip.output->close_audio(); if (!vorbis_ip.output-> open_audio(FMT_S16_NE, vi->rate, vi->channels)) { output_error = TRUE; vorbis_eos = TRUE; pthread_mutex_unlock(&vf_mutex); return current_section; } vorbis_ip.output->flush(ov_time_tell(&vf) * 1000); } } pthread_mutex_unlock(&vf_mutex); vorbis_ip.add_vis_pcm(vorbis_ip.output->written_time(), FMT_S16_NE, channels, bytes, pcmout); while (vorbis_ip.output->buffer_free() < bytes) { xmms_usleep(20000); if (!vorbis_playing) return current_section; if (seekneeded != -1) do_seek(); } vorbis_ip.output->write_audio(pcmout, bytes); return current_section;}static void *vorbis_play_loop(void *arg){ char *filename = (char *)arg; gchar *title = NULL; double time; long timercount = 0; vorbis_info *vi; int last_section = -1; FILE *stream = NULL; void *datasource = NULL; gboolean use_rg; float rg_scale; memset(&vf, 0, sizeof(vf)); if (strncasecmp("http://", filename, 7) != 0) { /* file is a real file */ if ((stream = fopen(filename, "r")) == NULL) { vorbis_eos = TRUE; goto play_cleanup; } datasource = (void *)stream; } else { /* file is a stream */ vorbis_is_streaming = 1; vorbis_http_open(filename); datasource = "NULL"; } /* * The open function performs full stream detection and * machine initialization. None of the rest of ov_xx() works * without it */ pthread_mutex_lock(&vf_mutex); if (ov_open_callbacks(datasource, &vf, NULL, 0, vorbis_callbacks) < 0) { vorbis_callbacks.close_func(datasource); pthread_mutex_unlock(&vf_mutex); vorbis_eos = TRUE; goto play_cleanup; } vi = ov_info(&vf, -1); if (vorbis_is_streaming) time = -1; else time = ov_time_total(&vf, -1) * 1000; if (vi->channels > 2) { vorbis_eos = TRUE; pthread_mutex_unlock(&vf_mutex); goto play_cleanup; } samplerate = vi->rate; channels = vi->channels; title = vorbis_generate_title(&vf, filename); use_rg = vorbis_update_replaygain(&rg_scale); vorbis_ip.set_info(title, time, ov_bitrate(&vf, -1), samplerate, channels); if (!vorbis_ip.output->open_audio(FMT_S16_NE, vi->rate, vi->channels)) { output_error = TRUE; pthread_mutex_unlock(&vf_mutex); goto play_cleanup; } pthread_mutex_unlock(&vf_mutex); seekneeded = -1; /* * Note that chaining changes things here; A vorbis file may * be a mix of different channels, bitrates and sample rates. * You can fetch the information for any section of the file * using the ov_ interface. */ while (vorbis_playing) { int current_section; if (seekneeded != -1) do_seek(); if (vorbis_eos) { xmms_usleep(20000); continue; } current_section = vorbis_process_data(last_section, use_rg, rg_scale); if (current_section != last_section) { /* * set total play time, bitrate, rate, and channels of * current section */ if (title) g_free(title); pthread_mutex_lock(&vf_mutex); title = vorbis_generate_title(&vf, filename); use_rg = vorbis_update_replaygain(&rg_scale); if (vorbis_is_streaming) time = -1; else time = ov_time_total(&vf, -1) * 1000; vorbis_ip.set_info(title, time, ov_bitrate(&vf, current_section), samplerate, channels);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -