📄 aq.c
字号:
/* TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp> Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi> 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 aq.c - Audio queue. Written by Masanao Izumo <mo@goice.co.jp>*/#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include <stdio.h>#include <stdlib.h>#ifdef HAVE_UNISTD_H#include <unistd.h>#endif /* HAVE_UNISTD_H */#ifndef NO_STRING_H#include <string.h>#else#include <strings.h>#endif#include "timidity.h"#include "common.h"#include "output.h"#include "aq.h"#include "timer.h"#include "controls.h"#include "miditrace.h"#include "instrum.h"#include "playmidi.h"#define TEST_SPARE_RATE 0.9#define MAX_BUCKET_TIME 0.2#define MAX_FILLED_TIME 2.0static int32 device_qsize;static int Bps; /* Bytes per sample frame */static int bucket_size;static int nbuckets = 0;static double bucket_time;int aq_fill_buffer_flag = 0;static int32 aq_start_count;static int32 aq_add_count;static int32 play_counter, play_offset_counter;static double play_start_time;typedef struct _AudioBucket{ char *data; int len; struct _AudioBucket *next;} AudioBucket;static AudioBucket *base_buckets = NULL;static AudioBucket *allocated_bucket_list = NULL;static AudioBucket *head = NULL;static AudioBucket *tail = NULL;static void alloc_soft_queue(void);static int add_play_bucket(const char *buf, int n);static void reuse_audio_bucket(AudioBucket *bucket);static AudioBucket *next_allocated_bucket(void);static void flush_buckets(void);static int aq_fill_one(void);static void aq_wait_ticks(void);static int32 estimate_queue_size(void);/* effect.c */extern void init_effect(void);extern int do_effect(int32* buf, int32 count);int aq_calc_fragsize(void){ int ch, bps, bs; double dq, bt; if(play_mode->encoding & PE_MONO) ch = 1; else ch = 2; if(play_mode->encoding & PE_24BIT) bps = ch * 3; else if(play_mode->encoding & PE_16BIT) bps = ch * 2; else bps = ch; bs = audio_buffer_size * bps; dq = play_mode->rate * MAX_FILLED_TIME * bps; while(bs * 2 > dq) bs /= 2; bt = (double)bs / bps / play_mode->rate; while(bt > MAX_BUCKET_TIME) { bs /= 2; bt = (double)bs / bps / play_mode->rate; } return bs;}void aq_setup(void){ int ch; /* Initialize Bps, bucket_size, device_qsize, and bucket_time */ if(play_mode->encoding & PE_MONO) ch = 1; else ch = 2; if(play_mode->encoding & PE_24BIT) Bps = 3 * ch; else if(play_mode->encoding & PE_16BIT) Bps = 2 * ch; else Bps = ch; if(play_mode->acntl(PM_REQ_GETFRAGSIZ, &bucket_size) == -1) bucket_size = audio_buffer_size * Bps; bucket_time = (double)bucket_size / Bps / play_mode->rate; if(IS_STREAM_TRACE) { if(play_mode->acntl(PM_REQ_GETQSIZ, &device_qsize) == -1) device_qsize = estimate_queue_size(); if(bucket_size * 2 > device_qsize) { ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Warning: Audio buffer is too small."); device_qsize = 0; } else { device_qsize -= device_qsize % Bps; /* Round Bps */ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Audio device queue size: %d bytes", device_qsize); ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Write bucket size: %d bytes (%d msec)", bucket_size, (int)(bucket_time * 1000 + 0.5)); } } else { device_qsize = 0; if(base_buckets) { free(base_buckets[0].data); free(base_buckets); base_buckets = NULL; } nbuckets = 0; } init_effect(); aq_add_count = 0;}void aq_set_soft_queue(double soft_buff_time, double fill_start_time){ static double last_soft_buff_time, last_fill_start_time; int nb; /* for re-initialize */ if(soft_buff_time < 0) soft_buff_time = last_soft_buff_time; if(fill_start_time < 0) fill_start_time = last_fill_start_time; nb = (int)(soft_buff_time / bucket_time); if(nb == 0) aq_start_count = 0; else aq_start_count = (int32)(fill_start_time * play_mode->rate); aq_fill_buffer_flag = (aq_start_count > 0); if(nbuckets != nb) { nbuckets = nb; alloc_soft_queue(); } last_soft_buff_time = soft_buff_time; last_fill_start_time = fill_start_time;}/* Estimates the size of audio device queue. * About sun audio, there are long-waiting if buffer of device audio is full. * So it is impossible to completely estimate the size. */static int32 estimate_queue_size(void){ char *nullsound; double tb, init_time, chunktime; int32 qbytes, max_qbytes; int ntries; nullsound = (char *)safe_malloc(bucket_size); memset(nullsound, 0, bucket_size); if(play_mode->encoding & (PE_ULAW|PE_ALAW)) general_output_convert((int32 *)nullsound, bucket_size/Bps); tb = play_mode->rate * Bps * TEST_SPARE_RATE; ntries = 1; max_qbytes = play_mode->rate * MAX_FILLED_TIME * Bps; retry: chunktime = (double)bucket_size / Bps / play_mode->rate; qbytes = 0; init_time = get_current_calender_time(); /* Start */ for(;;) { double start, diff; start = get_current_calender_time(); if(start - init_time > 1.0) /* ?? */ { ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "Warning: Audio test is terminated"); break; } play_mode->output_data(nullsound, bucket_size); diff = get_current_calender_time() - start; if(diff > chunktime/2 || qbytes > 1024*512 || chunktime < diff) break; qbytes += (int32)((chunktime - diff) * tb); if(qbytes > max_qbytes) { qbytes = max_qbytes; break; } } play_mode->acntl(PM_REQ_DISCARD, NULL); if(bucket_size * 2 > qbytes) { if(ntries == 4) { ctl->cmsg(CMSG_ERROR, VERB_NOISY, "Can't estimate audio queue length"); bucket_size = audio_buffer_size * Bps; free(nullsound); return 2 * audio_buffer_size * Bps; } ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "Retry to estimate audio queue length (%d times)", ntries); bucket_size /= 2; ntries++; goto retry; } free(nullsound); return qbytes;}/* Send audio data to play_mode->output_data() */static int aq_output_data(char *buff, int nbytes){ int i; play_counter += nbytes / Bps; while(nbytes > 0) { i = nbytes; if(i > bucket_size) i = bucket_size; if(play_mode->output_data(buff, i) == -1) return -1; nbytes -= i; buff += i; } return 0;}int aq_add(int32 *samples, int32 count){ int32 nbytes, i; char *buff; if(!(play_mode->flag & PF_PCM_STREAM)) return 0; if(!count) { if(!aq_fill_buffer_flag) return aq_fill_nonblocking(); return 0; } aq_add_count += count; do_effect(samples, count); nbytes = general_output_convert(samples, count); buff = (char *)samples; if(device_qsize == 0) return play_mode->output_data(buff, nbytes); aq_fill_buffer_flag = (aq_add_count <= aq_start_count); if(!aq_fill_buffer_flag) if(aq_fill_nonblocking() == -1) return -1; if(!ctl->trace_playing) { while((i = add_play_bucket(buff, nbytes)) < nbytes) { buff += i; nbytes -= i; if(head && head->len == bucket_size) { if(aq_fill_one() == -1) return -1; } aq_fill_buffer_flag = 0; } return 0; } trace_loop(); while((i = add_play_bucket(buff, nbytes)) < nbytes) { /* Software buffer is full. * Write one bucket to audio device. */ buff += i; nbytes -= i; aq_wait_ticks(); trace_loop(); if(aq_fill_nonblocking() == -1) return -1; aq_fill_buffer_flag = 0; } return 0;}/* alloc_soft_queue() (re-)initializes audio buckets. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -