63#define SAMPLE_RATE 48000
66#define FRAMES_PER_SEC 25
68#define SAMPLES_PER_FRAME (SAMPLE_RATE / FRAMES_PER_SEC)
70#define PARTICIPANT_TIMEOUT_S 60
77using namespace std::chrono;
81 bool operator() (
const sockaddr_storage & x,
const sockaddr_storage & y)
const {
82 if (x.ss_family != y.ss_family) {
83 return x.ss_family < y.ss_family;
86 if (x.ss_family == AF_INET) {
88 reinterpret_cast<const sockaddr_in &
>(x);
90 reinterpret_cast<const sockaddr_in &
>(y);
92 if (sin_x.sin_addr.s_addr != sin_y.sin_addr.s_addr) {
93 return sin_x.sin_addr.s_addr < sin_y.sin_addr.s_addr;
95 return sin_x.sin_port < sin_y.sin_port;
96 }
else if (x.ss_family == AF_INET6) {
98 reinterpret_cast<const sockaddr_in6 &
>(x);
100 reinterpret_cast<const sockaddr_in6 &
>(y);
102 for (
int i = 0; i < 16; ++i) {
103 if (sin_x.sin6_addr.s6_addr[i] != sin_y.sin6_addr.s6_addr[i]) {
104 return sin_x.sin6_addr.s6_addr[i] < sin_y.sin6_addr.s6_addr[i];
108 return sin_x.sin6_port < sin_y.sin6_port;
119 assert(l !=
nullptr && ss !=
nullptr);
122 struct sockaddr *sa = (
struct sockaddr *) ss;
123 assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
124 socklen_t
len = ss->ss_family == AF_INET ?
sizeof(
struct sockaddr_in) : sizeof(struct sockaddr_in6);
127 assert(m_network_device != NULL);
129 assert(m_tx_session != NULL);
132 if (!m_audio_coder) {
153 m_buffer = std::move(other.m_buffer);
157 other.m_audio_coder =
nullptr;
158 other.m_buffer =
nullptr;
159 other.m_tx_session =
nullptr;
160 other.m_network_device =
nullptr;
164 *
this = std::move(other);
173template<
typename source_t,
typename intermediate_t>
177 virtual intermediate_t
add_to_mix(intermediate_t dst, source_t sample) {
182 return mix - source_sample;
185 virtual intermediate_t
normalize(intermediate_t sample) = 0;
194template<
typename source_t,
typename intermediate_t>
197 intermediate_t
normalize(intermediate_t sample)
override {
199 return min<intermediate_t>(max<intermediate_t>(sample, numeric_limits<source_t>::min()), numeric_limits<source_t>::max());
210template<
typename source_t,
typename intermediate_t>
213 static constexpr double t = 0.5;
214 static constexpr double alpha = 5.71144;
215 intermediate_t
normalize(intermediate_t sample)
override {
216 if (sample >= numeric_limits<source_t>::min() / 2 &&
217 sample <= numeric_limits<source_t>::max() / 2) {
220 double sample_norm = (double) sample / numeric_limits<source_t>::max();
221 double ret = sample_norm / fabs(sample_norm) * (
t + (1.0 -
t) * log(1.0 +
alpha * (fabs(sample_norm) -
t) / (2 -
t)) / log(1.0 +
alpha)) * numeric_limits<source_t>::max();
230 shared_ptr<char> tmp(strdup(cfg), free);
231 char *
item, *save_ptr;
232 char *copy = tmp.get();
234 while ((
item = strtok_r(copy,
":", &save_ptr))) {
235 if (strncmp(
item,
"codec=", strlen(
"codec=")) == 0) {
237 }
else if (strncmp(
item,
"algo=", strlen(
"algo=")) == 0) {
238 string algo =
item + strlen(
"algo=");
239 if (
algo ==
"linear") {
241 }
else if (
algo ==
"logarithmic") {
274 map<sockaddr_storage, am_participant, sockaddr_storage_less>
participants;
287 chrono::steady_clock::time_point next_frame_time = chrono::steady_clock::now();
293 this_thread::sleep_until(next_frame_time);
294 next_frame_time += interval;
295 auto now = chrono::steady_clock::now();
298 if (next_frame_time < now) {
300 next_frame_time = now;
316 vector<audio_frame2> participant_frames(
participants.size());
317 int participant_index = 0;
322 static_assert(
CHANNELS == 1,
"Currently only one channel is implemented here.");
323 participant_frames[participant_index].resize(0, data_len_source);
324 char *particip_data = participant_frames[participant_index].get_data(0);
326 memset(particip_data, 0, data_len_source - ret);
329 auto dst = mixed.begin();
339 for (
auto & pb : participant_frames) {
340 auto mix = mixed.begin();
350 participant_index = 0;
352 audio_frame2 *uncompressed = &participant_frames[participant_index];
354 audio_tx_send(p.second.m_tx_session, p.second.m_network_device, &compressed);
355 uncompressed =
nullptr;
367 "\t%s -r mixer[:codec=<codec>][:algo={linear|logarithmic}]\n"
370 "\taudio codec to use\n"
372 "\tlinear sum of signals (with clamping)\n"
374 "\tlinear sum of signals to threshold, above threshold logarithmic dynamic range compression is used\n"
377 "1)\tYou do not need to specify audio participants explicitly,\n"
378 "\t" PACKAGE_NAME
" simply sends the the stream back to the host\n"
379 "\tthat is sending to mixer. Therefore it is necessary that the\n"
380 "\tparticipant uses single " PACKAGE_NAME
" for both sending and\n"
381 "\treceiving audio.\n"
382 "2)\tUses default port for receiving, therefore if you want to use it\n"
383 "\ton machine that is a part of the conference, you should use something like:\n"
384 "\t\t%s -s <your_capture> -P 5004:5004:5010:5006\n"
385 "\tfor the " PACKAGE_NAME
" instance that is part of the conference (not mixer!)\n",
392 *available_devices = NULL;
398 if (strcmp(cfg,
"help") == 0) {
415 auto ss = *(
struct sockaddr_storage *)
frame->network_source;
422 s->
participants.at(ss).last_seen = chrono::steady_clock::now();
448 if (*
len >=
sizeof(
bool)) {
449 *(
bool *) data =
true;
456 if (*
len !=
sizeof(
struct rtp *)) {
471 assert(
desc == requested);
struct video_frame * frame
int void(** deleter)(void *))
This file contains definition of some common audio types.
void audio_buffer_destroy(struct audio_buffer *buf)
struct audio_buffer * audio_buffer_init(int sample_rate, int bps, int ch_count, int suggested_latency_ms)
int audio_buffer_read(struct audio_buffer *buf, char *out, int max_len)
void audio_buffer_write(struct audio_buffer *buf, const char *in, int len)
#define AUDIO_PLAYBACK_CTL_QUERY_FORMAT
#define AUDIO_PLAYBACK_ABI_VERSION
#define AUDIO_PLAYBACK_CTL_MULTIPLE_STREAMS
#define AUDIO_PLAYBACK_PUT_NETWORK_DEVICE
virtual intermediate_t normalize(intermediate_t sample)=0
virtual intermediate_t get_mixed_without_source_sample(intermediate_t mix, source_t source_sample)
virtual intermediate_t add_to_mix(intermediate_t dst, source_t sample)
virtual ~generic_mix_algo()
intermediate_t normalize(intermediate_t sample) override
intermediate_t normalize(intermediate_t sample) override
static constexpr double alpha
static constexpr double t
bool operator()(const sockaddr_storage &x, const sockaddr_storage &y) const
struct audio_codec_state * audio_codec_init_cfg(const char *audio_codec_cfg, audio_codec_direction_t direction)
audio_frame2 audio_codec_compress(struct audio_codec_state *s, const audio_frame2 *frame)
void audio_codec_done(struct audio_codec_state *s)
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_WARNING
less severe errors
void module_done(struct module *module_data)
const char * get_commandline_param(const char *key)
@ LIBRARY_CLASS_AUDIO_PLAYBACK
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
static void audio_play_mixer_help()
#define PARTICIPANT_TIMEOUT_S
static void audio_play_mixer_done(void *state)
static void audio_play_mixer_put_frame(void *state, const struct audio_frame *frame)
static bool audio_play_mixer_reconfigure(void *state, struct audio_desc desc)
static void * audio_play_mixer_init(const char *cfg)
#define SAMPLES_PER_FRAME
static const struct audio_playback_info aplay_mixer_info
static bool audio_play_mixer_ctl(void *state, int request, void *data, size_t *len)
static void mixer_dummy_rtp_callback(struct rtp *session, rtp_event *e)
static void audio_play_mixer_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
int16_t sample_type_source
int32_t sample_type_mixed
rtp_t rtp_init_with_udp_socket(struct socket_udp_local *l, struct sockaddr *sa, socklen_t len, rtp_callback callback)
struct socket_udp_local * rtp_get_udp_local_socket(struct rtp *session)
void rtp_done(struct rtp *session)
struct rtp * m_network_device
struct audio_buffer * m_buffer
am_participant(am_participant &&other)
am_participant(struct socket_udp_local *l, struct sockaddr_storage *ss, string const &audio_codec)
chrono::steady_clock::time_point last_seen
am_participant & operator=(am_participant &&other)
struct audio_codec_state * m_audio_coder
struct used to comunicate with audio capture/playback device
struct socket_udp_local * recv_socket
unique_ptr< generic_mix_algo< sample_type_source, sample_type_mixed > > mixing_algorithm
map< sockaddr_storage, am_participant, sockaddr_storage_less > participants
state_audio_mixer & operator=(state_audio_mixer const &)=delete
state_audio_mixer(const char *cfg)
state_audio_mixer(state_audio_mixer const &)=delete
void set_thread_name(const char *name)
void audio_tx_send(struct tx *tx, struct rtp *rtp_session, const audio_frame2 *buffer)
struct tx * tx_init(struct module *parent, unsigned mtu, enum tx_media_type media_type, const char *fec, const char *encryption, long long int bitrate)