📄 mix.c
字号:
/*
* FILE: mix.c
* PROGRAM: RAT
* AUTHOR: Isidor Kouvelas
* MODIFIED BY: Orion Hodson + Colin Perkins
*
* Copyright (c) 1995-2001 University College London
* All rights reserved.
*/
#ifndef HIDE_SOURCE_STRINGS
static const char cvsid[] =
"$Id: mix.c,v 1.119 2002/03/15 17:57:17 ucacoxh Exp $";
#endif /* HIDE_SOURCE_STRINGS */
#include "config_unix.h"
#include "config_win32.h"
#include "memory.h"
#include "util.h"
#include "session.h"
#include "codec_types.h"
#include "codec.h"
#include "audio_util.h"
#include "audio_fmt.h"
#include "channel_types.h"
#include "pdb.h"
#include "mix.h"
#include "playout.h"
#include "debug.h"
#include "parameters.h"
#include "ui_send_audio.h"
#define MIX_MAGIC 0x81654620
struct s_mixer {
int buf_len; /* Length of circular buffer */
int head, tail; /* Index to head and tail samples in buffer */
timestamp_t head_time, tail_time; /* Time rep of head and tail */
int dist; /* Distance between head and tail. (for debug) */
sample *mix_buffer; /* The buffer containing mixed audio data. */
mixer_info_t info;
uint32_t magic; /* Debug check value */
};
typedef void (*mix_f)(sample *buf, sample *incoming, int len);
static mix_f audio_mix_fn;
static void
mix_verify(const mixer_t *ms)
{
#ifdef DEBUG
timestamp_t delta;
int dist;
assert((ms->head + ms->buf_len - ms->tail) % ms->buf_len == ms->dist);
assert(!ts_gt(ms->tail_time, ms->head_time));
assert(ms->dist <= ms->buf_len);
delta = ts_sub(ms->head_time, ms->tail_time);
dist = delta.ticks * ms->info.channels * ms->info.sample_rate / ts_get_freq(delta);
assert(abs((int)ms->dist - (int)dist) <= 1);
if (ts_eq(ms->head_time, ms->tail_time)) {
assert(ms->head == ms->tail);
}
#endif
assert(ms->magic == MIX_MAGIC);
}
/*
* Initialise the circular buffer that is used in mixing.
* The buffer length should be as big as the largest possible
* device cushion used (and maybe some more).
* We allocate space three times the requested one so that we
* dont have to copy everything when we hit the boundaries..
*/
int
mix_create(mixer_t **ppms,
const mixer_info_t *pmi,
timestamp_t now)
{
mixer_t *pms;
pms = (mixer_t *) xmalloc(sizeof(mixer_t));
if (pms) {
memset(pms, 0 , sizeof(mixer_t));
pms->magic = MIX_MAGIC;
memcpy(&pms->info, pmi, sizeof(mixer_info_t));
pms->buf_len = pms->info.buffer_length * pms->info.channels;
pms->mix_buffer = (sample *)xmalloc(3 * pms->buf_len * BYTES_PER_SAMPLE);
audio_zero(pms->mix_buffer, 3 * pms->info.buffer_length , DEV_S16);
pms->mix_buffer += pms->buf_len;
pms->head_time = pms->tail_time = ts_convert(pms->info.sample_rate, now);
*ppms = pms;
audio_mix_fn = audio_mix;
#ifdef WIN32
if (mmx_present()) {
audio_mix_fn = audio_mix_mmx;
}
#endif /* WIN32 */
mix_verify(pms);
debug_msg("Mixer created. Aligned to %d %dkHz\n", now.ticks, ts_get_freq(now));
return TRUE;
}
return FALSE;
}
void
mix_destroy(mixer_t **ppms)
{
mixer_t *pms;
assert(ppms);
pms = *ppms;
assert(pms);
mix_verify(pms);
debug_msg("Mixer destroyed. Head %d %dkHz Tail %d %dkHz\n",
pms->head_time.ticks, ts_get_freq(pms->head_time),
pms->tail_time.ticks, ts_get_freq(pms->tail_time));
xfree(pms->mix_buffer - pms->buf_len); /* yuk! ouch! splat! */
xfree(pms);
*ppms = NULL;
}
static void
mix_zero(mixer_t *ms, int offset, int len)
{
assert(len <= ms->buf_len);
if (offset + len > ms->buf_len) {
audio_zero(ms->mix_buffer + offset, ms->buf_len - offset, DEV_S16);
audio_zero(ms->mix_buffer, offset + len-ms->buf_len, DEV_S16);
} else {
audio_zero(ms->mix_buffer + offset, len, DEV_S16);
}
xmemchk();
}
static void
mix_advance_head(mixer_t *ms, timestamp_t new_head_time)
{
timestamp_t delta;
int zeros;
mix_verify(ms);
assert(ts_gt(new_head_time, ms->head_time));
delta = ts_sub(new_head_time, ms->head_time);
zeros = delta.ticks * ms->info.channels * ms->info.sample_rate / ts_get_freq(delta);
mix_zero(ms, ms->head, zeros);
ms->dist += zeros;
ms->head += zeros;
ms->head %= ms->buf_len;
ms->head_time = new_head_time;
mix_verify(ms);
}
/* mix_put_audio mixes a single audio frame into mix buffer. It returns
* TRUE if incoming audio frame is compatible with mix, FALSE
* otherwise. */
int
mix_put_audio(mixer_t *ms,
pdb_entry_t *pdbe,
coded_unit *frame,
timestamp_t playout)
{
sample *samples;
int32_t pos;
uint32_t nticks, nsamples;
uint16_t channels;
uint32_t rate;
timestamp_t frame_period, playout_end, delta;
mix_verify(ms);
if (!codec_get_native_info(frame->id, &rate, &channels)) {
debug_msg("Cannot mix non-native media\n");
abort();
}
if (rate != ms->info.sample_rate || channels != ms->info.channels) {
/* This should only occur if source changes sample rate
* mid-stream and before buffering runs dry in end host.
* This should be a very rare event.
*/
debug_msg("Unit (%d, %d) not compitible with mix (%d, %d).\n",
rate,
channels,
ms->info.sample_rate,
ms->info.channels);
return FALSE;
}
assert(rate == (uint32_t)ms->info.sample_rate);
assert(channels == (uint32_t)ms->info.channels);
playout = ts_convert(ms->info.sample_rate, playout);
nticks = frame->data_len / (sizeof(sample) * channels);
frame_period = ts_map32(rate, nticks);
/* Map frame period to mixer time base, otherwise we can get
* truncation errors in verification of mixer when sample rate
* conversion is active. */
frame_period = ts_convert(ms->info.sample_rate, frame_period);
if (pdbe->first_mix) {
debug_msg("New mix\n");
pdbe->next_mix = playout;
pdbe->first_mix = 0;
}
mix_verify(ms);
if (ts_gt(ms->tail_time, playout)) {
debug_msg("playout before tail (%d %dkHz < %d %dkHz)\n",
playout.ticks, ts_get_freq(playout),
ms->tail_time.ticks, ts_get_freq(ms->tail_time));
}
samples = (sample*)frame->data;
nsamples = frame->data_len / sizeof(sample);
/* Advance head if necessary */
playout_end = ts_add(playout, ts_map32(ms->info.sample_rate, nsamples / ms->info.channels));
if (ts_gt(playout_end, ms->head_time)) {
uint32_t playout_delta = timestamp_to_ms(ts_sub(playout_end, ms->head_time));
if (playout_delta > 1000) {
debug_msg("WARNING: Large playout buffer advancement (%dms)\n", playout_delta);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -