Compare commits

...

12 Commits

Author SHA1 Message Date
15f0fcda69 add link to buseqchannelcell 2025-06-15 23:50:24 +01:00
738688a8a7
Merge pull request #16 from wcyoung08/add-to-bus-class
Extends BusEQclass with BusEQChCell, giving access to all bus eq channel cell parameters.
2025-06-15 23:47:15 +01:00
1509afd4f5 add 2.7.0 to CHANGELOG 2025-06-15 23:42:23 +01:00
7232ba6248 add eqedit poe script
minor bump
2025-06-15 23:38:11 +01:00
1ff2017d51 iterate over cells. 2025-06-15 23:32:47 +01:00
William Young
fe1f4ee324 Updated example script to be sure other params work, updated readme and changed channel number from 9 to 8 2025-06-15 16:59:17 -05:00
4953751c02 instantiate types
bump poethepoet
2025-06-15 22:32:46 +01:00
William Young
abbbf57982 Added some logic to test but changes seem to work now 2025-06-15 15:43:41 -05:00
714d2fc972 pass channel + cell indices to each class
update identifier properties to reflect changes.
2025-06-15 20:03:11 +01:00
c797912458 set cell count to 6 (0 up to 5) 2025-06-15 20:02:08 +01:00
William Young
f702b4feb3 Got rid of error with channels and cells not being subscriptable, but now getting -3 error trying to set eq.channel[0].cell[0].on 2025-06-15 11:48:17 -05:00
William Young
f8f10e358f Initial setup adding classes for channels and cells 2025-06-15 10:43:50 -05:00
8 changed files with 200 additions and 4 deletions

View File

@ -11,11 +11,18 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
- [x]
## [2.7.0] - 2025-06-15
### Added
- Bus.EQ Channel Cell commands added, see [Bus.EQ.Channel.Cell](https://github.com/onyx-and-iris/voicemeeter-api-python?tab=readme-ov-file#buseqchannelcell).
- Added by [PR #16](https://github.com/onyx-and-iris/voicemeeter-api-python/pull/16)
## [2.6.0] - 2024-06-29
### Added
- bits kwarg for overriding the type of GUI that is launched on startup.
- bits kwarg for overriding the type of GUI that is launched on startup.
- Defaults to 64, set it to either 32 or 64.
### Fixed

View File

@ -292,6 +292,23 @@ example:
vm.bus[3].eq.on = True
```
##### Bus.EQ.Channel.Cell
The following properties are available.
- `on`: boolean
- `type`: int
- `f`: float
- `gain`: float
- `q`: quality
example:
```python
vm.bus[3].eq.channel[0].cell[2].on = True
vm.bus[3].eq.channel[0].cell[2].f = 5000
```
##### Bus.Modes
The following properties are available.

View File

@ -0,0 +1,9 @@
## About
The purpose of this script is to demonstratehow to utilize the channels and cells that are available as part of the EQ. It should take audio playing in the second virtual strip and then apply a LGF on the first physical at 500 Hz.
## Use
Configured for banana version.
Make sure you are playing audio into the second virtual strip and out of the first physical bus, both channels are unmuted and that you aren't monitoring another mixbus. Then run the script.

View File

@ -0,0 +1,50 @@
import time
import voicemeeterlib
def main():
KIND_ID = 'banana'
BUS_INDEX = 0 # Index of the bus to edit, can be changed as needed
CHANNEL_INDEX = 0 # Index of the channel to edit, can be changed as needed
with voicemeeterlib.api(KIND_ID) as vm:
print(f'Bus[{BUS_INDEX}].EQ.on: {vm.bus[BUS_INDEX].eq.on}')
print(
f'Bus[{BUS_INDEX}].EQ.channel[{CHANNEL_INDEX}].cell[0].on: {vm.bus[BUS_INDEX].eq.channel[CHANNEL_INDEX].cell[0].on}'
)
print('Check sending commands (should affect your VM Banana window)')
vm.bus[BUS_INDEX].eq.on = True
vm.bus[BUS_INDEX].eq.ab = 0 # corresponds to A EQ memory slot
vm.bus[BUS_INDEX].mute = False
for j, cell in enumerate(vm.bus[BUS_INDEX].eq.channel[CHANNEL_INDEX].cell):
cell.on = True
cell.f = 500
cell.gain = -10
cell.type = 3 # Should correspond to LPF
cell.q = 10
print(
f'Channel {CHANNEL_INDEX}, Cell {j}: on={cell.on}, f={cell.f}, type={cell.type}, gain={cell.gain}, q={cell.q}'
)
time.sleep(1) # Sleep to simulate processing time
cell.on = False
cell.f = 50
cell.gain = 0
cell.type = 0
cell.q = 3
print(
f'Channel {CHANNEL_INDEX}, Cell {j}: on={cell.on}, f={cell.f}, type={cell.type} , gain={cell.gain}, q={cell.q}'
)
vm.bus[BUS_INDEX].eq.on = False
if __name__ == '__main__':
main()

View File

@ -1,6 +1,6 @@
[project]
name = "voicemeeter-api"
version = "2.6.1"
version = "2.7.0"
description = "A Python wrapper for the Voiceemeter API"
authors = [
{name = "Onyx and Iris",email = "code@onyxandiris.online"}
@ -16,7 +16,7 @@ dependencies = [
packages = [{ include = "voicemeeterlib" }]
[tool.poetry.requires-plugins]
poethepoet = "^0.32.1"
poethepoet = "^0.35.0"
[tool.poetry.group.dev.dependencies]
pytest = "^8.3.4"
@ -37,6 +37,7 @@ levels.script = "scripts:ex_levels"
midi.script = "scripts:ex_midi"
obs.script = "scripts:ex_obs"
observer.script = "scripts:ex_observer"
eqedit.script = "scripts:ex_eqedit"
test-basic.script = "scripts:test_basic"
test-banana.script = "scripts:test_banana"
test-potato.script = "scripts:test_potato"

View File

@ -37,6 +37,11 @@ def ex_observer():
subprocess.run([sys.executable, str(scriptpath)])
def ex_eqedit():
scriptpath = Path.cwd() / 'examples' / 'eq_edit' / '.'
subprocess.run([sys.executable, str(scriptpath)])
def test_basic():
subprocess.run(['tox'], env=os.environ.copy() | {'KIND': 'basic'})

View File

@ -88,6 +88,25 @@ class Bus(IRemote):
class BusEQ(IRemote):
@classmethod
def make(cls, remote, i):
"""
Factory method for BusEQ.
Returns a BusEQ class.
"""
kls = (cls,)
BusEQ_cls = type(
'BusEQ',
kls,
{
'channel': tuple(
BusEQCh.make(remote, i, j) for j in range(remote.kind.channels)
)
},
)
return BusEQ_cls(remote, i)
@property
def identifier(self) -> str:
return f'Bus[{self.index}].eq'
@ -109,6 +128,86 @@ class BusEQ(IRemote):
self.setter('ab', 1 if val else 0)
class BusEQCh(IRemote):
@classmethod
def make(cls, remote, i, j):
"""
Factory method for Bus EQ channel.
Returns a BusEQCh class.
"""
kls = (cls,)
BusEQCh_cls = type(
'BusEQCh',
kls,
{
'cell': tuple(
BusEQChCell(remote, i, j, k) for k in range(remote.kind.cells)
)
},
)
return BusEQCh_cls(remote, i, j)
def __init__(self, remote, i, j):
super().__init__(remote, i)
self.channel_index = j
@property
def identifier(self) -> str:
return f'Bus[{self.index}].eq.channel[{self.channel_index}]'
class BusEQChCell(IRemote):
def __init__(self, remote, i, j, k):
super().__init__(remote, i)
self.channel_index = j
self.cell_index = k
@property
def identifier(self) -> str:
return f'Bus[{self.index}].eq.channel[{self.channel_index}].cell[{self.cell_index}]'
@property
def on(self) -> bool:
return self.getter('on') == 1
@on.setter
def on(self, val: bool):
self.setter('on', 1 if val else 0)
@property
def type(self) -> int:
return int(self.getter('type'))
@type.setter
def type(self, val: int):
self.setter('type', val)
@property
def f(self) -> float:
return round(self.getter('f'), 1)
@f.setter
def f(self, val: float):
self.setter('f', val)
@property
def gain(self) -> float:
return round(self.getter('gain'), 1)
@gain.setter
def gain(self, val: float):
self.setter('gain', val)
@property
def q(self) -> float:
return round(self.getter('q'), 1)
@q.setter
def q(self, val: float):
self.setter('q', val)
class PhysicalBus(Bus):
@classmethod
def make(cls, remote, i, kind):
@ -321,7 +420,7 @@ def bus_factory(is_phys_bus, remote, i) -> Union[PhysicalBus, VirtualBus]:
{
'levels': BusLevel(remote, i),
'mode': BUSMODEMIXIN_cls(remote, i),
'eq': BusEQ(remote, i),
'eq': BusEQ.make(remote, i),
},
)(remote, i)

View File

@ -31,6 +31,8 @@ class KindMapClass(metaclass=SingletonType):
asio: tuple
insert: int
composite: int
channels: int
cells: int
@property
def phys_in(self) -> int:
@ -76,6 +78,8 @@ class BasicMap(KindMapClass):
asio: tuple = (0, 0)
insert: int = 0
composite: int = 0
channels: int = 0
cells: int = 0
@dataclass(frozen=True)
@ -86,6 +90,8 @@ class BananaMap(KindMapClass):
asio: tuple = (6, 8)
insert: int = 22
composite: int = 8
channels: int = 8
cells: int = 6
@dataclass(frozen=True)
@ -96,6 +102,8 @@ class PotatoMap(KindMapClass):
asio: tuple = (10, 8)
insert: int = 34
composite: int = 8
channels: int = 8
cells: int = 6
def kind_factory(kind_id):