diff --git a/soundio/soundio.h b/soundio/soundio.h index e5b8c8d..02618e0 100644 --- a/soundio/soundio.h +++ b/soundio/soundio.h @@ -536,7 +536,8 @@ struct SoundIoOutStream { /// For JACK, this value is always equal to /// SoundIoDevice::software_latency_current of the device. double software_latency; - + /// Core Audio and WASAPI only: current output Audio Unit volume. Float, 0.0-1.0. + float volume; /// Defaults to NULL. Put whatever you want here. void *userdata; /// In this callback, you call ::soundio_outstream_begin_write and @@ -1057,6 +1058,9 @@ SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, b SOUNDIO_EXPORT int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, double *out_latency); +SOUNDIO_EXPORT int soundio_outstream_set_volume(struct SoundIoOutStream *outstream, + double volume); + // Input Streams diff --git a/src/coreaudio.c b/src/coreaudio.c index 3079f4e..8f55a67 100644 --- a/src/coreaudio.c +++ b/src/coreaudio.c @@ -1045,6 +1045,11 @@ static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamP return SoundIoErrorOpeningDevice; } + if ((os_err = AudioUnitGetParameter (osca->instance, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &outstream->volume))) { + outstream_destroy_ca(si, os); + return SoundIoErrorOpeningDevice; + } + osca->hardware_latency = dca->latency_frames / (double)outstream->sample_rate; return 0; @@ -1115,6 +1120,18 @@ static int outstream_get_latency_ca(struct SoundIoPrivate *si, struct SoundIoOut return 0; } +static int outstream_set_volume_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, float volume) { + struct SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio; + struct SoundIoOutStream *outstream = &os->pub; + + OSStatus os_err; + if ((os_err = AudioUnitSetParameter (osca->instance, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, volume, 0))) { + return SoundIoErrorIncompatibleDevice; + } + outstream->volume = volume; + return 0; +} + static OSStatus on_instream_device_overload(AudioObjectID in_object_id, UInt32 in_number_addresses, const AudioObjectPropertyAddress in_addresses[], void *in_client_data) { @@ -1456,6 +1473,7 @@ int soundio_coreaudio_init(struct SoundIoPrivate *si) { si->outstream_clear_buffer = outstream_clear_buffer_ca; si->outstream_pause = outstream_pause_ca; si->outstream_get_latency = outstream_get_latency_ca; + si->outstream_set_volume = outstream_set_volume_ca; si->instream_open = instream_open_ca; si->instream_destroy = instream_destroy_ca; diff --git a/src/coreaudio.h b/src/coreaudio.h index 784a71c..8515f21 100644 --- a/src/coreaudio.h +++ b/src/coreaudio.h @@ -52,6 +52,7 @@ struct SoundIoOutStreamCoreAudio { int frames_left; int write_frame_count; double hardware_latency; + float volume; struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; }; diff --git a/src/soundio.c b/src/soundio.c index 32d3455..2486888 100644 --- a/src/soundio.c +++ b/src/soundio.c @@ -272,6 +272,7 @@ void soundio_disconnect(struct SoundIo *soundio) { si->outstream_clear_buffer = NULL; si->outstream_pause = NULL; si->outstream_get_latency = NULL; + si->outstream_set_volume = NULL; si->instream_open = NULL; si->instream_destroy = NULL; @@ -560,6 +561,13 @@ int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, double *ou return si->outstream_get_latency(si, os, out_latency); } +int soundio_outstream_set_volume(struct SoundIoOutStream *outstream, double volume) { + struct SoundIo *soundio = outstream->device->soundio; + struct SoundIoPrivate *si = (struct SoundIoPrivate *)soundio; + struct SoundIoOutStreamPrivate *os = (struct SoundIoOutStreamPrivate *)outstream; + return si->outstream_set_volume(si, os, volume); +} + static void default_instream_error_callback(struct SoundIoInStream *is, int err) { soundio_panic("libsoundio: %s", soundio_strerror(err)); } diff --git a/src/soundio_private.h b/src/soundio_private.h index 213b36f..aea991f 100644 --- a/src/soundio_private.h +++ b/src/soundio_private.h @@ -152,7 +152,7 @@ struct SoundIoPrivate { int (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause); int (*outstream_get_latency)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, double *out_latency); - + int (*outstream_set_volume)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, float volume); int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); diff --git a/src/wasapi.c b/src/wasapi.c index 5556a6b..cc7c698 100644 --- a/src/wasapi.c +++ b/src/wasapi.c @@ -1181,6 +1181,8 @@ static void force_device_scan_wasapi(struct SoundIoPrivate *si) { static void outstream_thread_deinit(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { struct SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi; + if (osw->audio_volume_control) + IUnknown_Release(osw->audio_volume_control); if (osw->audio_render_client) IUnknown_Release(osw->audio_render_client); if (osw->audio_session_control) @@ -1378,6 +1380,17 @@ static int outstream_do_open(struct SoundIoPrivate *si, struct SoundIoOutStreamP return SoundIoErrorOpeningDevice; } + if (FAILED(hr = IAudioClient_GetService(osw->audio_client, IID_ISimpleAudioVolume, + (void **)&osw->audio_volume_control))) + { + return SoundIoErrorOpeningDevice; + } + + if (FAILED(hr = osw->audio_volume_control->GetMasterVolume(&volume))) + { + return SoundIoErrorOpeningDevice; + } + return 0; } @@ -1706,6 +1719,21 @@ static int outstream_get_latency_wasapi(struct SoundIoPrivate *si, struct SoundI return 0; } +static int outstream_set_volume_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, float volume) +{ + struct SoundIoOutStream *outstream = &os->pub; + struct SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi; + + HRESULT hr; + if (FAILED(hr = osw->audio_volume_control->SetMasterVolume(&volume))) + { + return SoundIoErrorIncompatibleDevice; + } + + outstream->volume = volume; + return 0; +} + static void instream_thread_deinit(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { struct SoundIoInStreamWasapi *isw = &is->backend_data.wasapi; @@ -2295,6 +2323,7 @@ int soundio_wasapi_init(struct SoundIoPrivate *si) { si->outstream_clear_buffer = outstream_clear_buffer_wasapi; si->outstream_pause = outstream_pause_wasapi; si->outstream_get_latency = outstream_get_latency_wasapi; + si->outstream_set_volume = outstream_set_volume_wasapi; si->instream_open = instream_open_wasapi; si->instream_destroy = instream_destroy_wasapi; diff --git a/src/wasapi.h b/src/wasapi.h index e27b3f8..8dff116 100644 --- a/src/wasapi.h +++ b/src/wasapi.h @@ -56,6 +56,7 @@ struct SoundIoOutStreamWasapi { IAudioClockAdjustment *audio_clock_adjustment; IAudioRenderClient *audio_render_client; IAudioSessionControl *audio_session_control; + ISimpleAudioVolume *audio_volume_control; LPWSTR stream_name; bool need_resample; struct SoundIoOsThread *thread; @@ -76,6 +77,7 @@ struct SoundIoOutStreamWasapi { int open_err; bool started; UINT32 min_padding_frames; + float volume; struct SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; };