⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mix.c

📁 将每一个声源加到混音缓冲器,经过处理后返回
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -