|
@@ -139,95 +139,6 @@ static bool process_audio_delay(struct audio_monitor *monitor, float **data,
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-static void on_audio_playback(void *param, obs_source_t *source,
|
|
|
- const struct audio_data *audio_data, bool muted)
|
|
|
-{
|
|
|
- struct audio_monitor *monitor = param;
|
|
|
- IAudioRenderClient *render = monitor->render;
|
|
|
- uint8_t *resample_data[MAX_AV_PLANES];
|
|
|
- float vol = source->user_volume;
|
|
|
- uint32_t resample_frames;
|
|
|
- uint64_t ts_offset;
|
|
|
- bool success;
|
|
|
- BYTE *output;
|
|
|
-
|
|
|
- if (!TryAcquireSRWLockExclusive(&monitor->playback_mutex)) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (os_atomic_load_long(&source->activate_refs) == 0) {
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
-
|
|
|
- success = audio_resampler_resample(
|
|
|
- monitor->resampler, resample_data, &resample_frames, &ts_offset,
|
|
|
- (const uint8_t *const *)audio_data->data,
|
|
|
- (uint32_t)audio_data->frames);
|
|
|
- if (!success) {
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
-
|
|
|
- UINT32 pad = 0;
|
|
|
- monitor->client->lpVtbl->GetCurrentPadding(monitor->client, &pad);
|
|
|
-
|
|
|
- bool decouple_audio = source->async_unbuffered &&
|
|
|
- source->async_decoupled;
|
|
|
-
|
|
|
- if (monitor->source_has_video && !decouple_audio) {
|
|
|
- uint64_t ts = audio_data->timestamp - ts_offset;
|
|
|
-
|
|
|
- if (!process_audio_delay(monitor, (float **)(&resample_data[0]),
|
|
|
- &resample_frames, ts, pad)) {
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- HRESULT hr =
|
|
|
- render->lpVtbl->GetBuffer(render, resample_frames, &output);
|
|
|
- if (FAILED(hr)) {
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
-
|
|
|
- if (!muted) {
|
|
|
- /* apply volume */
|
|
|
- if (!close_float(vol, 1.0f, EPSILON)) {
|
|
|
- register float *cur = (float *)resample_data[0];
|
|
|
- register float *end =
|
|
|
- cur + resample_frames * monitor->channels;
|
|
|
-
|
|
|
- while (cur < end)
|
|
|
- *(cur++) *= vol;
|
|
|
- }
|
|
|
- memcpy(output, resample_data[0],
|
|
|
- resample_frames * monitor->channels * sizeof(float));
|
|
|
- }
|
|
|
-
|
|
|
- render->lpVtbl->ReleaseBuffer(render, resample_frames,
|
|
|
- muted ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
|
|
|
-
|
|
|
-unlock:
|
|
|
- ReleaseSRWLockExclusive(&monitor->playback_mutex);
|
|
|
-}
|
|
|
-
|
|
|
-static inline void audio_monitor_free(struct audio_monitor *monitor)
|
|
|
-{
|
|
|
- if (monitor->ignore)
|
|
|
- return;
|
|
|
-
|
|
|
- if (monitor->source) {
|
|
|
- obs_source_remove_audio_capture_callback(
|
|
|
- monitor->source, on_audio_playback, monitor);
|
|
|
- }
|
|
|
-
|
|
|
- if (monitor->client)
|
|
|
- monitor->client->lpVtbl->Stop(monitor->client);
|
|
|
-
|
|
|
- safe_release(monitor->client);
|
|
|
- safe_release(monitor->render);
|
|
|
- audio_resampler_destroy(monitor->resampler);
|
|
|
- circlebuf_free(&monitor->delay_buffer);
|
|
|
- da_free(monitor->buf);
|
|
|
-}
|
|
|
-
|
|
|
static enum speaker_layout convert_speaker_layout(DWORD layout, WORD channels)
|
|
|
{
|
|
|
switch (layout) {
|
|
@@ -246,37 +157,14 @@ static enum speaker_layout convert_speaker_layout(DWORD layout, WORD channels)
|
|
|
return (enum speaker_layout)channels;
|
|
|
}
|
|
|
|
|
|
-extern bool devices_match(const char *id1, const char *id2);
|
|
|
-
|
|
|
-static bool audio_monitor_init(struct audio_monitor *monitor,
|
|
|
- obs_source_t *source)
|
|
|
+static bool audio_monitor_init_wasapi(struct audio_monitor *monitor)
|
|
|
{
|
|
|
+ bool success = false;
|
|
|
IMMDeviceEnumerator *immde = NULL;
|
|
|
WAVEFORMATEX *wfex = NULL;
|
|
|
- bool success = false;
|
|
|
UINT32 frames;
|
|
|
HRESULT hr;
|
|
|
|
|
|
- monitor->source = source;
|
|
|
-
|
|
|
- const char *id = obs->audio.monitoring_device_id;
|
|
|
- if (!id) {
|
|
|
- warn("%s: No device ID set", __FUNCTION__);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {
|
|
|
- obs_data_t *s = obs_source_get_settings(source);
|
|
|
- const char *s_dev_id = obs_data_get_string(s, "device_id");
|
|
|
- bool match = devices_match(s_dev_id, id);
|
|
|
- obs_data_release(s);
|
|
|
-
|
|
|
- if (match) {
|
|
|
- monitor->ignore = true;
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/* ------------------------------------------ *
|
|
|
* Init device */
|
|
|
|
|
@@ -289,6 +177,7 @@ static bool audio_monitor_init(struct audio_monitor *monitor,
|
|
|
}
|
|
|
|
|
|
IMMDevice *device = NULL;
|
|
|
+ const char *const id = obs->audio.monitoring_device_id;
|
|
|
if (strcmp(id, "default") == 0) {
|
|
|
hr = immde->lpVtbl->GetDefaultAudioEndpoint(immde, eRender,
|
|
|
eConsole, &device);
|
|
@@ -378,8 +267,6 @@ static bool audio_monitor_init(struct audio_monitor *monitor,
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
|
- InitializeSRWLock(&monitor->playback_mutex);
|
|
|
-
|
|
|
success = true;
|
|
|
|
|
|
fail:
|
|
@@ -389,6 +276,125 @@ fail:
|
|
|
return success;
|
|
|
}
|
|
|
|
|
|
+static void on_audio_playback(void *param, obs_source_t *source,
|
|
|
+ const struct audio_data *audio_data, bool muted)
|
|
|
+{
|
|
|
+ struct audio_monitor *monitor = param;
|
|
|
+ IAudioRenderClient *render = monitor->render;
|
|
|
+ uint8_t *resample_data[MAX_AV_PLANES];
|
|
|
+ float vol = source->user_volume;
|
|
|
+ uint32_t resample_frames;
|
|
|
+ uint64_t ts_offset;
|
|
|
+ bool success;
|
|
|
+ BYTE *output;
|
|
|
+
|
|
|
+ if (!TryAcquireSRWLockExclusive(&monitor->playback_mutex)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (os_atomic_load_long(&source->activate_refs) == 0) {
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ success = audio_resampler_resample(
|
|
|
+ monitor->resampler, resample_data, &resample_frames, &ts_offset,
|
|
|
+ (const uint8_t *const *)audio_data->data,
|
|
|
+ (uint32_t)audio_data->frames);
|
|
|
+ if (!success) {
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ UINT32 pad = 0;
|
|
|
+ monitor->client->lpVtbl->GetCurrentPadding(monitor->client, &pad);
|
|
|
+
|
|
|
+ bool decouple_audio = source->async_unbuffered &&
|
|
|
+ source->async_decoupled;
|
|
|
+
|
|
|
+ if (monitor->source_has_video && !decouple_audio) {
|
|
|
+ uint64_t ts = audio_data->timestamp - ts_offset;
|
|
|
+
|
|
|
+ if (!process_audio_delay(monitor, (float **)(&resample_data[0]),
|
|
|
+ &resample_frames, ts, pad)) {
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ HRESULT hr =
|
|
|
+ render->lpVtbl->GetBuffer(render, resample_frames, &output);
|
|
|
+ if (FAILED(hr)) {
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!muted) {
|
|
|
+ /* apply volume */
|
|
|
+ if (!close_float(vol, 1.0f, EPSILON)) {
|
|
|
+ register float *cur = (float *)resample_data[0];
|
|
|
+ register float *end =
|
|
|
+ cur + resample_frames * monitor->channels;
|
|
|
+
|
|
|
+ while (cur < end)
|
|
|
+ *(cur++) *= vol;
|
|
|
+ }
|
|
|
+ memcpy(output, resample_data[0],
|
|
|
+ resample_frames * monitor->channels * sizeof(float));
|
|
|
+ }
|
|
|
+
|
|
|
+ render->lpVtbl->ReleaseBuffer(render, resample_frames,
|
|
|
+ muted ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
|
|
|
+
|
|
|
+unlock:
|
|
|
+ ReleaseSRWLockExclusive(&monitor->playback_mutex);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void audio_monitor_free(struct audio_monitor *monitor)
|
|
|
+{
|
|
|
+ if (monitor->ignore)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (monitor->source) {
|
|
|
+ obs_source_remove_audio_capture_callback(
|
|
|
+ monitor->source, on_audio_playback, monitor);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (monitor->client)
|
|
|
+ monitor->client->lpVtbl->Stop(monitor->client);
|
|
|
+
|
|
|
+ safe_release(monitor->client);
|
|
|
+ safe_release(monitor->render);
|
|
|
+ audio_resampler_destroy(monitor->resampler);
|
|
|
+ circlebuf_free(&monitor->delay_buffer);
|
|
|
+ da_free(monitor->buf);
|
|
|
+}
|
|
|
+
|
|
|
+extern bool devices_match(const char *id1, const char *id2);
|
|
|
+
|
|
|
+static bool audio_monitor_init(struct audio_monitor *monitor,
|
|
|
+ obs_source_t *source)
|
|
|
+{
|
|
|
+ monitor->source = source;
|
|
|
+
|
|
|
+ const char *id = obs->audio.monitoring_device_id;
|
|
|
+ if (!id) {
|
|
|
+ warn("%s: No device ID set", __FUNCTION__);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {
|
|
|
+ obs_data_t *s = obs_source_get_settings(source);
|
|
|
+ const char *s_dev_id = obs_data_get_string(s, "device_id");
|
|
|
+ bool match = devices_match(s_dev_id, id);
|
|
|
+ obs_data_release(s);
|
|
|
+
|
|
|
+ if (match) {
|
|
|
+ monitor->ignore = true;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ InitializeSRWLock(&monitor->playback_mutex);
|
|
|
+
|
|
|
+ return audio_monitor_init_wasapi(monitor);
|
|
|
+}
|
|
|
+
|
|
|
static void audio_monitor_init_final(struct audio_monitor *monitor)
|
|
|
{
|
|
|
if (monitor->ignore)
|