Browse Source

libobs: Add flag to force source audio to mono

This flag is actually useful under a number of circumstances, and has
been requested a number of times.
jp9000 11 years ago
parent
commit
63c43b649d
2 changed files with 57 additions and 1 deletions
  1. 55 1
      libobs/obs-source.c
  2. 2 0
      libobs/obs.h

+ 55 - 1
libobs/obs-source.c

@@ -1471,10 +1471,58 @@ static inline void copy_audio_data(obs_source_t *source,
 		source->audio_storage_size = size;
 }
 
+/* TODO: SSE optimization */
+static void downmix_to_mono_planar(struct obs_source *source, uint32_t frames)
+{
+	uint32_t channels = get_audio_channels(source->sample_info.speakers);
+	const float channels_i = 1.0f / (float)channels;
+	float **data = (float**)source->audio_data.data;
+
+	for (uint32_t channel = 1; channel < channels; channel++) {
+		for (uint32_t frame = 0; frame < frames; frame++)
+			data[0][frame] += data[channel][frame];
+	}
+
+	for (uint32_t frame = 0; frame < frames; frame++)
+		data[0][frame] *= channels_i;
+
+	for (uint32_t channel = 1; channel < channels; channel++) {
+		for (uint32_t frame = 0; frame < frames; frame++)
+			data[channel][frame] = data[0][frame];
+	}
+}
+
+static void downmix_to_mono_interleaved(struct obs_source *source,
+		uint32_t frames)
+{
+	uint32_t channels = get_audio_channels(source->sample_info.speakers);
+	const float channels_i = 1.0f / (float)channels;
+	float *data = (float*)source->audio_data.data[0];
+
+	for (uint32_t frame = 0; frame < frames; frame++) {
+		uint32_t pos = frame * channels;
+
+		for (uint32_t channel = 1; channel < channels; channel++)
+			data[pos] += data[pos + channel];
+	}
+
+	for (uint32_t frame = 0; frame < frames; frame++)
+		data[frame * channels] *= channels_i;
+
+	for (uint32_t frame = 0; frame < frames; frame++) {
+		uint32_t pos = frame * channels;
+
+		for (uint32_t channel = 1; channel < channels; channel++)
+			data[pos + channel] = data[pos];
+	}
+}
+
 /* resamples/remixes new audio to the designated main audio output format */
 static void process_audio(obs_source_t *source,
 		const struct obs_source_audio *audio)
 {
+	uint32_t frames = audio->frames;
+
 	if (source->sample_info.samples_per_sec != audio->samples_per_sec ||
 	    source->sample_info.format          != audio->format          ||
 	    source->sample_info.speakers        != audio->speakers)
@@ -1485,7 +1533,6 @@ static void process_audio(obs_source_t *source,
 
 	if (source->resampler) {
 		uint8_t  *output[MAX_AV_PLANES];
-		uint32_t frames;
 		uint64_t offset;
 
 		memset(output, 0, sizeof(output));
@@ -1500,6 +1547,13 @@ static void process_audio(obs_source_t *source,
 		copy_audio_data(source, audio->data, audio->frames,
 				audio->timestamp);
 	}
+
+	if ((source->flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0) {
+		if (is_audio_planar(source->sample_info.format))
+			downmix_to_mono_planar(source, frames);
+		else
+			downmix_to_mono_interleaved(source, frames);
+	}
 }
 
 void obs_source_output_audio(obs_source_t *source,

+ 2 - 0
libobs/obs.h

@@ -753,6 +753,8 @@ EXPORT void obs_source_load(obs_source_t *source);
 
 /** Specifies that async video frames should be played as soon as possible */
 #define OBS_SOURCE_FLAG_UNBUFFERED             (1<<0)
+/** Specifies to force audio to mono */
+#define OBS_SOURCE_FLAG_FORCE_MONO             (1<<1)
 
 /** Sets source flags.  Note that these are different from the main output
  * flags. */