?? core.c
字號:
/* -*- linux-c -*- Copyright (C) 2004 Tom Szilagyi 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., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: core.c,v 1.57 2006/10/02 17:50:26 peterszilagyi Exp $*/#include <config.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <fcntl.h>#include <math.h>#include <time.h>#include <getopt.h>#include <time.h>#include <sys/time.h>#include <sys/stat.h>#include <libxml/xmlmemory.h>#include <libxml/parser.h>#ifdef _WIN32#include <glib.h>#else#include <pthread.h>#endif /* _WIN32 */#ifdef HAVE_SRC#include <samplerate.h>#endif /* HAVE_SRC */#ifdef HAVE_OSS#include <sys/ioctl.h>#include <sys/types.h>#ifdef __FreeBSD__#include <sys/soundcard.h>#else#include <linux/soundcard.h>#endif /* __FreeBSD__ */#endif /* HAVE_OSS */#ifdef HAVE_JACK#include <sys/mman.h>#include <jack/jack.h>#endif /* HAVE_JACK */#ifdef _WIN32#include <windows.h>#include <mmsystem.h>#endif /* _WIN32 */#include "common.h"#include "version.h"#include "rb.h"#include "options.h"#include "decoder/file_decoder.h"#include "transceiver.h"#include "gui_main.h"#include "plugin.h"#include "i18n.h"#include "core.h"extern options_t options;/* JACK data */#ifdef HAVE_JACKjack_client_t * jack_client;jack_port_t * out_L_port;jack_port_t * out_R_port;char * client_name = NULL;u_int32_t jack_nframes;int jack_is_shutdown;int auto_connect = 0;int default_ports = 1;char * user_port1 = NULL;char * user_port2 = NULL;#endif /* HAVE_JACK */const size_t sample_size = sizeof(float);gint playlist_state, browser_state;/* ALSA driver parameters */#ifdef HAVE_ALSAint nperiods = 0;int period = 0;#endif /* HAVE_ALSA *//* The name of the output device e.g. "/dev/dsp" or "plughw:0,0" */char * device_name = NULL;/***** disk thread stuff *****/unsigned long long sample_offset;status_t disk_thread_status;int output = 0; /* oss/alsa/jack */#ifdef HAVE_SRCint src_type = 4;int src_type_parsed = 0;#endif /* HAVE_SRC *//* Synchronization between disk thread and output thread */AQUALUNG_MUTEX_DECLARE_INIT(disk_thread_lock)AQUALUNG_COND_DECLARE_INIT(disk_thread_wake)rb_t * rb; /* this is the audio stream carrier ringbuffer */rb_t * rb_disk2out;/* Communication between gui thread and disk thread */rb_t * rb_gui2disk;rb_t * rb_disk2gui;/* Lock critical operations that could interfere with output thread */double left_gain = 1.0;double right_gain = 1.0;/* LADSPA stuff */float * l_buf = NULL;float * r_buf = NULL;#ifdef HAVE_LADSPAvolatile int plugin_lock = 0;unsigned long ladspa_buflen = 0;int n_plugins = 0;plugin_instance * plugin_vect[MAX_PLUGINS];#endif /* HAVE_LADSPA *//* remote control */extern int immediate_start;extern int aqualung_session_id;extern int aqualung_socket_fd;extern char aqualung_socket_filename[256];float convf(char * s) { float val, pow; int i, sign; for (i = 0; s[i] == ' ' || s[i] == '\n' || s[i] == '\t'; i++); sign = 1; if (s[i] == '+' || s[i] == '-') sign = (s[i++] == '+') ? 1 : -1; for (val = 0; s[i] >= '0' && s[i] <= '9'; i++) val = 10 * val + s[i] - '0'; if ((s[i] == '.') || (s[i] == ',')) i++; for (pow = 1; s[i] >= '0' && s[i] <= '9'; i++) { val = 10 * val + s[i] - '0'; pow *= 10; } return(sign * val / pow);}/* return 1 if conversion is possible, 0 if not */intsample_rates_ok(int out_SR, int file_SR) {#ifdef HAVE_SRC float src_ratio; src_ratio = 1.0 * out_SR / file_SR; if (!src_is_valid_ratio(src_ratio) || src_ratio > MAX_RATIO || src_ratio < 1.0/MAX_RATIO) { fprintf(stderr, "core.c/sample_rates_ok(): too big difference between input and " "output sample rate!\n");#else if (out_SR != file_SR) { fprintf(stderr, "Input file's samplerate (%d Hz) and output samplerate (%d Hz) differ, " "and\nAqualung is compiled without Sample Rate Converter support. To play " "this file,\nyou have to build Aqualung with internal Sample Rate Converter " "support,\nor set the playback sample rate to match the file's sample rate." "\n", file_SR, out_SR); #endif /* HAVE_SRC */ return 0; } return 1;}void *disk_thread(void * arg) { thread_info_t * info = (thread_info_t *)arg; file_decoder_t * fdec = NULL; unsigned int n_read = 0; unsigned int want_read; int n_src = 0; int n_src_prev = 0; int end_of_file = 0; double src_ratio = 1.0; void * readbuf = malloc(MAX_RATIO * info->rb_size * 2 * sample_size); void * framebuf = malloc(MAX_RATIO * info->rb_size * 2 * sample_size); size_t n_space; char send_cmd, recv_cmd; char filename[RB_CONTROL_SIZE]; seek_t seek; cue_t cue; int i;#ifdef HAVE_SRC int src_type_prev; SRC_STATE * src_state; SRC_DATA src_data; int src_error; if ((src_state = src_new(src_type, 2, &src_error)) == NULL) { fprintf(stderr, "disk thread: error: src_new() failed: %s.\n", src_strerror(src_error)); exit(1); } src_type_prev = src_type;#endif /* HAVE_SRC */ if ((fdec = file_decoder_new()) == NULL) { fprintf(stderr, "disk thread: error: file_decoder_new() failed\n"); exit(1); } if ((!readbuf) || (!framebuf)) { fprintf(stderr, "disk thread: malloc error\n"); exit(1); } AQUALUNG_MUTEX_LOCK(disk_thread_lock) filename[0] = '\0'; while (1) { recv_cmd = 0; if (rb_read_space(rb_gui2disk) > 0) { rb_read(rb_gui2disk, &recv_cmd, 1); switch (recv_cmd) { case CMD_CUE: /* read the string */ while (rb_read_space(rb_gui2disk) < sizeof(cue_t)) ; rb_read(rb_gui2disk, (void *)&cue, sizeof(cue_t)); if (cue.filename != NULL) { strncpy(filename, cue.filename, MAXLEN-1); free(cue.filename); } else { filename[0] = '\0'; } if (fdec->file_lib != 0) file_decoder_close(fdec); if (filename[0] != '\0') { if (file_decoder_open(fdec, filename)) { fdec->samples_left = 0; info->is_streaming = 0; end_of_file = 1; send_cmd = CMD_FILEREQ; rb_write(rb_disk2gui, &send_cmd, 1); goto sleep; } else if (!sample_rates_ok(info->out_SR, fdec->SR)) { fdec->file_open = 1; /* to get close_file() working */ file_decoder_close(fdec); fdec->file_open = 0; fdec->samples_left = 0; info->is_streaming = 0; end_of_file = 1; send_cmd = CMD_FILEREQ; rb_write(rb_disk2gui, &send_cmd, 1); goto sleep; } else { file_decoder_set_rva(fdec, cue.voladj); info->in_SR_prev = info->in_SR; info->in_SR = fdec->SR; info->is_mono = (fdec->channels == 1) ? 1 : 0; sample_offset = 0; send_cmd = CMD_FILEINFO; rb_write(rb_disk2gui, &send_cmd, sizeof(send_cmd)); rb_write(rb_disk2gui, (char *)&(fdec->fileinfo), sizeof(fileinfo_t)); info->is_streaming = 1; end_of_file = 0; } } else { /* STOP */ info->is_streaming = 0; /* send a FLUSH command to output thread to stop immediately */ send_cmd = CMD_FLUSH; rb_write(rb_disk2out, &send_cmd, 1); goto sleep; } break; case CMD_STOPWOFL: /* STOP but first flush output ringbuffer. */ info->is_streaming = 0; goto flush; break; case CMD_PAUSE: info->is_streaming = 0; /* send a FLUSH command to output thread */ send_cmd = CMD_FLUSH; rb_write(rb_disk2out, &send_cmd, 1); /* roll back sample_offset samples, if possible */ sample_offset = rb_read_space(rb) / (2 * sample_size) * src_ratio; if (sample_offset < fdec->fileinfo.total_samples - fdec->samples_left) file_decoder_seek(fdec, fdec->fileinfo.total_samples - fdec->samples_left - sample_offset); else file_decoder_seek(fdec, 0); break; case CMD_RESUME: info->is_streaming = 1; break; case CMD_FINISH: /* send FINISH to output thread, then goto exit */ send_cmd = CMD_FINISH; rb_write(rb_disk2out, &send_cmd, 1); goto done; break; case CMD_SEEKTO: while (rb_read_space(rb_gui2disk) < sizeof(seek_t)) ; rb_read(rb_gui2disk, (char *)&seek, sizeof(seek_t)); if (fdec->file_lib != 0) { file_decoder_seek(fdec, seek.seek_to_pos); /* send a FLUSH command to output thread */ send_cmd = CMD_FLUSH; rb_write(rb_disk2out, &send_cmd, 1); } else { /* send dummy STATUS to gui, to set pos slider to zero */ disk_thread_status.samples_left = 0; disk_thread_status.sample_offset = 0; send_cmd = CMD_STATUS; rb_write(rb_disk2gui, &send_cmd, sizeof(send_cmd)); rb_write(rb_disk2gui, (char *)&disk_thread_status, sizeof(status_t)); } break; default: fprintf(stderr, "disk thread: received unexpected command %d\n", recv_cmd); break; } } else if (end_of_file) goto sleep; if (!info->is_streaming) goto sleep; n_space = rb_write_space(rb) / (2 * sample_size); while (n_src < 0.95 * n_space) { src_ratio = (double)info->out_SR / (double)info->in_SR; n_src_prev = n_src; want_read = floor((n_space - n_src) / src_ratio); if (want_read > MAX_RATIO * info->rb_size) want_read = MAX_RATIO * info->rb_size; n_read = file_decoder_read(fdec, readbuf, want_read); if (n_read < want_read) end_of_file = 1; if (info->is_mono) { /* convert to stereo interleaved */ for (i = 2*n_read - 1; i >= 0; i--) { memcpy(readbuf + i * sample_size, readbuf + i/2 * sample_size, sample_size); } } if (info->in_SR == info->out_SR) { memcpy(framebuf + n_src_prev * 2*sample_size, readbuf, n_read * 2*sample_size); n_src += n_read; } else { /* do SRC */#ifdef HAVE_SRC if ((info->in_SR_prev != info->in_SR) || (src_type_prev != src_type)) { /* reinit SRC */ src_state = src_delete(src_state); if ((src_state = src_new(src_type, 2, &src_error)) == NULL) { fprintf(stderr, "disk thread: error: src_new() failed: " "%s.\n", src_strerror(src_error)); goto done; } info->in_SR_prev = info->in_SR; src_type_prev = src_type; } src_data.input_frames = n_read; src_data.data_in = readbuf; src_data.src_ratio = src_ratio; src_data.data_out = framebuf + n_src_prev * 2*sample_size; src_data.output_frames = n_space - n_src_prev; if ((src_error = src_process(src_state, &src_data))) { fprintf(stderr, "disk thread: SRC error: %s\n", src_strerror(src_error)); goto done; } n_src += src_data.output_frames_gen;#endif /* HAVE_SRC */ } if (end_of_file) { file_decoder_close(fdec); /* send request for a new filename */ send_cmd = CMD_FILEREQ; rb_write(rb_disk2gui, &send_cmd, 1); goto sleep; } } flush: rb_write(rb, framebuf, n_src * 2*sample_size); /* update & send STATUS */ fdec->samples_left -= n_read; sample_offset = rb_read_space(rb) / (2 * sample_size); disk_thread_status.samples_left = fdec->samples_left; disk_thread_status.sample_offset = sample_offset / src_ratio; if (disk_thread_status.samples_left < 0) { disk_thread_status.samples_left = 0; } if (!rb_read_space(rb_gui2disk)) { send_cmd = CMD_STATUS; rb_write(rb_disk2gui, &send_cmd, sizeof(send_cmd)); rb_write(rb_disk2gui, (char *)&disk_thread_status, sizeof(status_t)); } /* cleanup buffer counters */ n_src = 0; n_src_prev = 0; end_of_file = 0; sleep: { /* suspend thread, wake up after 100 ms */#ifdef _WIN32 GTimeVal time; GTimeVal * timeout = &time; g_get_current_time(timeout); g_time_val_add(timeout, 100000);#else struct timeval now; struct timezone tz; struct timespec timeout; gettimeofday(&now, &tz); timeout.tv_nsec = now.tv_usec * 1000 + 100000000; timeout.tv_sec = now.tv_sec; while (timeout.tv_nsec > 1000000000) { timeout.tv_nsec -= 1000000000; timeout.tv_sec += 1; }#endif /* _WIN32 */ AQUALUNG_COND_TIMEDWAIT(disk_thread_wake, disk_thread_lock, timeout) } } done: free(readbuf); free(framebuf);#ifdef HAVE_SRC src_state = src_delete(src_state);#endif /* HAVE_SRC */ file_decoder_delete(fdec); AQUALUNG_MUTEX_UNLOCK(disk_thread_lock) return 0;}/* OSS output thread */#ifdef HAVE_OSSvoid *oss_thread(void * arg) { u_int32_t i; thread_info_t * info = (thread_info_t *)arg; int bufsize = 1024; int n_avail; int ioctl_status; char recv_cmd; int fd_oss = info->fd_oss; short * oss_short_buf; struct timespec req_time; struct timespec rem_time; req_time.tv_sec = 0; req_time.tv_nsec = 100000000;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -