<commit_before>/*-
 * Copyright 2011-2013 <NAME>
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <TINYMIXER/mixer.h>
#include <stdint.h>

#if TINYMIXER_SUPPORT_VORBIS_STREAM
#include <TINYVORBIS/vorbis.h>
#endif

#ifndef tinymixer_sqrtf
#include <math.h>
#define tinymixer_sqrtf sqrtf
#endif

#ifndef tinymixer_abs
#include <stdlib.h>
#define tinymixer_abs abs
#endif

#ifndef tinymixer_fabs
#include <math.h>
#define tinymixer_fabs fabs
#endif

#if !defined(tinymixer_free) || !defined(tinymixer_alloc)
#include <stdlib.h>
#define tinymixer_alloc malloc
#define tinymixer_free free
#endif

#if !defined(tinymixer_memset)
#include <string.h>
#define tinymixer_memset memset
#endif

#if !defined(tinymixer_memcpy)
#include <string.h>
#define tinymixer_memcpy memcpy
#endif

namespace {
struct SourceFlags {
	enum Enum {
		Playing		= 1 << 0,
		Positional	= 1 << 1,
		Looping		= 1 << 2,
		FadeOut		= 1 << 3,
		Frequency	= 1 << 4,
	};
};

struct Buffer;
struct Source;

struct buffer_functions {
	void (*on_destroy)(Buffer* buffer);

	void (*start_source)(Source* source);
	void (*end_source)(Source* source);
	int (*request_samples)(Source* source, const float** left, const float** right, int nsamples);

	int (*get_buffer_size)(const Buffer* buffer);
};

struct Buffer {
	const buffer_functions *funcs;
	int32_t refcnt;
};

struct static_source_data {
	int32_t sample_pos;
};

#if TINYMIXER_SUPPORT_VORBIS_STREAM
struct vorbis_stream_data {
	stb_vorbis* v;
	const float* outputs[2];
	int noutputs;
};
#endif

struct Source {
	const Buffer* buffer;
	union {
		static_source_data static_source;
#if TINYMIXER_SUPPORT_VORBIS_STREAM
		vorbis_stream_data vorbis_stream;
#endif
	} instance_data;
	float position[3];
	float fadeout_per_sample;
	float gain_base;
	float distance_min;
	float distance_difference;
	float frequency;
	uint16_t frame_age;
	uint8_t flags;
	uint8_t gain_base_index;
};

static const int c_ngaintypes = 8;
static const int c_nsources = 32;
static const int c_nsamples = 2048;
static const float c_fnsamples = (float)c_nsamples;
static float c_speakerdist = 0.17677669529663688110021109052621f; // 1/(4 *sqrtf(2))

struct Mixer {
	tinymixer_callback callback;
	float position[3];
	float gain_master;
	float gain_base[c_ngaintypes];
	float gain_callback;
	int32_t sample_rate;
	float compressor_last_samples[2];
	float compressor_thresholds[2];
	float compressor_multipliers[2];
	float compressor_factor;
	float compressor_attack_per1ksamples;
	float compressor_release_per1ksamples;
	int32_t samples_remaining;
	Source sources[c_nsources];
	float buffer[2*c_nsamples];
	float scratch[2*c_nsamples];
};
}

static const int c_quantize = 1024;
static const int c_quantize_mask = (c_quantize - 1);
static Mixer g_mixer;

static inline float mixer_clamp(float v, float min, float max) { return min > v ? min : (v > max ? max : v); }
static inline int32_t mixer_clamp(int32_t v, int32_t min, int32_t max) { return min > v ? min : (v > max ? max : v); }
static inline int mixer_min(int a, int b) { return (a < b) ? a : b; }
static inline int mixer_max(int a, int b) { return (b < a) ? a : b; }
static inline float mixer_dist(const float* a, const float* b) {
	const float distsq = (a[0] - b[0])*(a[0] - b[0]) + (a[1] - b[1])*(a[1] - b[1]) + (a[2] - b[2])*(a[2] - b[2]);
	return tinymixer_sqrtf(distsq);
}
static inline void mixer_vcopy(float* v, const float* a) { v[0] = a[0], v[1] = a[1], v[2] = a[2]; }

static void addref(Buffer* buffer) {
	++buffer->refcnt;
}

static void decref(Buffer* buffer) {
	if (0 == --buffer->refcnt) {
		buffer->funcs->on_destroy(buffer);
		tinymixer_free(buffer);
	}
}

static void kill_source(Source* source) {
	source->buffer->funcs->end_source(source);

	decref((Buffer*)source->buffer);
	source->buffer = 0;
	source->flags = 0;
}

static Source *find_source() {
	Source* best_source = 0;
	uint16_t best_age = 0xffff;

	for (int ii = 0; ii < c_nsources; ++ii) {
		Source* source = &g_mixer.sources[ii];
		if (!source->buffer)
			return source;

		if (0 == (source->flags & SourceFlags::Looping)) {
			const uint16_t age = source->frame_age;
			if (age < best_age) {
				best_source = source;
				best_age = age;
			}
		}
	}

	if (NULL != best_source) {
		if (best_source->buffer) {
			kill_source(best_source);
		}
	}
	return best_source;
}

static void resample_mono(float* out, int nout, const float* in, int qfreq) {
	for (int qpos = 0; nout; --nout, qpos += qfreq) {
		const int qindex = qpos / c_quantize;
		const int qinterp = qpos % c_quantize;

		*out++ = in[qindex] + (float)qinterp * (in[qindex + 1] - in[qindex]) / (float)c_quantize;
	}
}

static void render(Source* source, float* buffer, const float gain[2]) {

	float* left = buffer;
	float* right = buffer + c_nsamples;

	int remaining = c_nsamples;<commit_msg>The oldest sound sound be evicted first<commit_after>/*-
 * Copyright 2011-2013 <NAME>
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <TINYMIXER/mixer.h>
#include <stdint.h>

#if TINYMIXER_SUPPORT_VORBIS_STREAM
#include <TINYVORBIS/vorbis.h>
#endif

#ifndef tinymixer_sqrtf
#include <math.h>
#define tinymixer_sqrtf sqrtf
#endif

#ifndef tinymixer_abs
#include <stdlib.h>
#define tinymixer_abs abs
#endif

#ifndef tinymixer_fabs
#include <math.h>
#define tinymixer_fabs fabs
#endif

#if !defined(tinymixer_free) || !defined(tinymixer_alloc)
#include <stdlib.h>
#define tinymixer_alloc malloc
#define tinymixer_free free
#endif

#if !defined(tinymixer_memset)
#include <string.h>
#define tinymixer_memset memset
#endif

#if !defined(tinymixer_memcpy)
#include <string.h>
#define tinymixer_memcpy memcpy
#endif

namespace {
struct SourceFlags {
	enum Enum {
		Playing		= 1 << 0,
		Positional	= 1 << 1,
		Looping		= 1 << 2,
		FadeOut		= 1 << 3,
		Frequency	= 1 << 4,
	};
};

struct Buffer;
struct Source;

struct buffer_functions {
	void (*on_destroy)(Buffer* buffer);

	void (*start_source)(Source* source);
	void (*end_source)(Source* source);
	int (*request_samples)(Source* source, const float** left, const float** right, int nsamples);

	int (*get_buffer_size)(const Buffer* buffer);
};

struct Buffer {
	const buffer_functions *funcs;
	int32_t refcnt;
};

struct static_source_data {
	int32_t sample_pos;
};

#if TINYMIXER_SUPPORT_VORBIS_STREAM
struct vorbis_stream_data {
	stb_vorbis* v;
	const float* outputs[2];
	int noutputs;
};
#endif

struct Source {
	const Buffer* buffer;
	union {
		static_source_data static_source;
#if TINYMIXER_SUPPORT_VORBIS_STREAM
		vorbis_stream_data vorbis_stream;
#endif
	} instance_data;
	float position[3];
	float fadeout_per_sample;
	float gain_base;
	float distance_min;
	float distance_difference;
	float frequency;
	uint16_t frame_age;
	uint8_t flags;
	uint8_t gain_base_index;
};

static const int c_ngaintypes = 8;
static const int c_nsources = 32;
static const int c_nsamples = 2048;
static const float c_fnsamples = (float)c_nsamples;
static float c_speakerdist = 0.17677669529663688110021109052621f; // 1/(4 *sqrtf(2))

struct Mixer {
	tinymixer_callback callback;
	float position[3];
	float gain_master;
	float gain_base[c_ngaintypes];
	float gain_callback;
	int32_t sample_rate;
	float compressor_last_samples[2];
	float compressor_thresholds[2];
	float compressor_multipliers[2];
	float compressor_factor;
	float compressor_attack_per1ksamples;
	float compressor_release_per1ksamples;
	int32_t samples_remaining;
	Source sources[c_nsources];
	float buffer[2*c_nsamples];
	float scratch[2*c_nsamples];
};
}

static const int c_quantize = 1024;
static const int c_quantize_mask = (c_quantize - 1);
static Mixer g_mixer;

static inline float mixer_clamp(float v, float min, float max) { return min > v ? min : (v > max ? max : v); }
static inline int32_t mixer_clamp(int32_t v, int32_t min, int32_t max) { return min > v ? min : (v > max ? max : v); }
static inline int mixer_min(int a, int b) { return (a < b) ? a : b; }
static inline int mixer_max(int a, int b) { return (b < a) ? a : b; }
static inline float mixer_dist(const float* a, const float* b) {
	const float distsq = (a[0] - b[0])*(a[0] - b[0]) + (a[1] - b[1])*(a[1] - b[1]) + (a[2] - b[2])*(a[2] - b[2]);
	return tinymixer_sqrtf(distsq);
}
static inline void mixer_vcopy(float* v, const float* a) { v[0] = a[0], v[1] = a[1], v[2] = a[2]; }

static void addref(Buffer* buffer) {
	++buffer->refcnt;
}

static void decref(Buffer* buffer) {
	if (0 == --buffer->refcnt) {
		buffer->funcs->on_destroy(buffer);
		tinymixer_free(buffer);
	}
}

static void kill_source(Source* source) {
	source->buffer->funcs->end_source(source);

	decref((Buffer*)source->buffer);
	source->buffer = 0;
	source->flags = 0;
}

static Source *find_source() {
	Source* best_source = 0;
	uint16_t best_age = 0;

	for (int ii = 0; ii < c_nsources; ++ii) {
		Source* source = &g_mixer.sources[ii];
		if (!source->buffer)
			return source;

		if (0 == (source->flags & SourceFlags::Looping)) {
			const uint16_t age = source->frame_age;
			if (age >= best_age) {
				best_source = source;
				best_age = age;
			}
		}
	}

	if (NULL != best_source) {
		if (best_source->buffer) {
			kill_source(best_source);
		}
	}
	return best_source;
}

static void resample_mono(float* out, int nout, const float* in, int qfreq) {
	for (int qpos = 0; nout; --nout, qpos += qfreq) {
		const int qindex = qpos / c_quantize;
		const int qinterp = qpos % c_quantize;

		*out++ = in[qindex] + (float)qinterp * (in[qindex + 1] - in[qindex]) / (float)c_quantize;
	}
}

static void render(Source* source, float* buffer, const float gain[2]) {

	float* left = buffer;
	float* right = buffer + c_nsamples;

	int remaining = c_nsamples;
{'fraction_non_alphanumeric': 0.11196879150066401, 'fraction_numerical': 0.02158034528552457, 'mean_word_length': 4.696926713947991, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1169973', 'n_tokens_mistral': 4555, 'n_tokens_neox': 4239, 'n_words': 1297}