Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hs.audiodevice:thru() and hs.audiodevice:setThru(thru) #3716

Merged
merged 1 commit into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Hammerspoon Tests/HSaudiodevice.m
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ - (void)testMute {
RUN_LUA_TEST()
}

- (void)testThru {
RUN_LUA_TEST()
}

- (void)testVolume {
SKIP_IN_TRAVIS()
RUN_LUA_TEST()
Expand Down
90 changes: 90 additions & 0 deletions extensions/audiodevice/libaudiodevice.m
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,94 @@

}

/// hs.audiodevice:thru() -> bool or nil
/// Method
/// Get the play through (low latency/direct monitoring) state of the audio device
///
/// Parameters:
/// * None
///
/// Returns:
/// * True if the audio device has thru enabled, False if thru is disabled, nil if it does not support thru
///
/// Notes:
/// * This method only works on devices that have hardware support (often microphones with a built-in headphone jack)
/// * This setting corresponds to the "Thru" setting in Audio MIDI Setup
static int audiodevice_thru(lua_State* L) {
LuaSkin *skin = [LuaSkin sharedWithState:L];
[skin checkArgs:LS_TUSERDATA, USERDATA_TAG, LS_TBREAK];

audioDeviceUserData *audioDevice = userdataToAudioDevice(L, 1);
AudioDeviceID deviceId = audioDevice->deviceId;
unsigned int scope;
UInt32 thru;
UInt32 thruSize = sizeof(UInt32);

if (isOutputDevice(deviceId)) {
scope = kAudioObjectPropertyScopeOutput;

Check warning on line 1189 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1189

Added line #L1189 was not covered by tests
} else {
scope = kAudioObjectPropertyScopeInput;
}

AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyPlayThru,
scope,
kAudioObjectPropertyElementMain
};

if (AudioObjectHasProperty(deviceId, &propertyAddress) && (AudioObjectGetPropertyData(deviceId, &propertyAddress, 0, NULL, &thruSize, &thru) == noErr)) {
lua_pushboolean(L, thru != 0);

Check warning on line 1201 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1201

Added line #L1201 was not covered by tests
} else {
lua_pushnil(L);
}

return 1;
}

/// hs.audiodevice:setThru(thru) -> bool
/// Method
/// Set the play through (low latency/direct monitoring) state of the audio device
///
/// Parameters:
/// * thru - A boolean value. True to enable thru, False to disable
///
/// Returns:
/// * True if thru was set, False if the audio device does not support thru
///
/// Notes:
/// * This method only works on devices that have hardware support (often microphones with a built-in headphone jack)
/// * This setting corresponds to the "Thru" setting in Audio MIDI Setup
static int audiodevice_setThru(lua_State* L) {
LuaSkin *skin = [LuaSkin sharedWithState:L];
[skin checkArgs:LS_TUSERDATA, USERDATA_TAG, LS_TBOOLEAN, LS_TBREAK];

Check warning on line 1224 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1222-L1224

Added lines #L1222 - L1224 were not covered by tests

audioDeviceUserData *audioDevice = userdataToAudioDevice(L, 1);
AudioDeviceID deviceId = audioDevice->deviceId;
unsigned int scope;
UInt32 thru = lua_toboolean(L, 2);
UInt32 thruSize = sizeof(UInt32);

Check warning on line 1230 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1226-L1230

Added lines #L1226 - L1230 were not covered by tests

if (isOutputDevice(deviceId)) {
scope = kAudioObjectPropertyScopeOutput;
} else {
scope = kAudioObjectPropertyScopeInput;
}

Check warning on line 1236 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1232-L1236

Added lines #L1232 - L1236 were not covered by tests

AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyPlayThru,
scope,
kAudioObjectPropertyElementMain
};

Check warning on line 1242 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1238-L1242

Added lines #L1238 - L1242 were not covered by tests

if (AudioObjectHasProperty(deviceId, &propertyAddress) && (AudioObjectSetPropertyData(deviceId, &propertyAddress, 0, NULL, thruSize, &thru) == noErr)) {
lua_pushboolean(L, TRUE);
} else {
lua_pushboolean(L, FALSE);
}

Check warning on line 1248 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1244-L1248

Added lines #L1244 - L1248 were not covered by tests

return 1;
}

Check warning on line 1251 in extensions/audiodevice/libaudiodevice.m

View check run for this annotation

Codecov / codecov/patch

extensions/audiodevice/libaudiodevice.m#L1250-L1251

Added lines #L1250 - L1251 were not covered by tests

/// hs.audiodevice:isOutputDevice() -> boolean
/// Method
/// Determines if an audio device is an output device
Expand Down Expand Up @@ -1900,6 +1988,8 @@
{"setVolume", audiodevice_setvolume},
{"balance", audiodevice_balance},
{"setBalance", audiodevice_setbalance},
{"thru", audiodevice_thru},
{"setThru", audiodevice_setThru},
{"setInputVolume", audiodevice_setInputVolume},
{"setOutputVolume", audiodevice_setOutputVolume},
{"muted", audiodevice_muted},
Expand Down
15 changes: 15 additions & 0 deletions extensions/audiodevice/test_audiodevice.lua
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,21 @@ function testMute()
return success()
end

function testThru()
local device = hs.audiodevice.defaultInputDevice()
local wasThru = device:thru()
if (type(wasThru) ~= "boolean") then
-- This device does not support thru. Not much we can do about it, so log it and move on
print("Audiodevice does not support thru, unable to test thru functionality. Skipping test due to lack of hardware")
return success()
end
device:setThru(not wasThru)
assertIsEqual(not wasThru, device:thru())
-- Be nice to whoever is running the test and restore the original state
device:setThru(wasThru)
return success()
end

function testJackConnected()
local jackConnected = hs.audiodevice.defaultOutputDevice():jackConnected()
if (type(jackConnected) ~= "boolean") then
Expand Down
Loading