mirror of
https://github.com/onyx-and-iris/voicemeeter-api-python.git
synced 2025-06-27 14:50:23 +01:00
Merge pull request #16 from wcyoung08/add-to-bus-class
Extends BusEQclass with BusEQChCell, giving access to all bus eq channel cell parameters.
This commit is contained in:
commit
738688a8a7
@ -11,6 +11,13 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
|||||||
|
|
||||||
- [x]
|
- [x]
|
||||||
|
|
||||||
|
## [2.7.0] - 2025-06-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Bus.EQ Channel Cell commands added, see [Bus.EQ.Channel.Cell]().
|
||||||
|
- Added by [PR #16](https://github.com/onyx-and-iris/voicemeeter-api-python/pull/16)
|
||||||
|
|
||||||
## [2.6.0] - 2024-06-29
|
## [2.6.0] - 2024-06-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
17
README.md
17
README.md
@ -292,6 +292,23 @@ example:
|
|||||||
vm.bus[3].eq.on = True
|
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
|
##### Bus.Modes
|
||||||
|
|
||||||
The following properties are available.
|
The following properties are available.
|
||||||
|
9
examples/eq_edit/README.md
Normal file
9
examples/eq_edit/README.md
Normal 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.
|
50
examples/eq_edit/__main__.py
Normal file
50
examples/eq_edit/__main__.py
Normal 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()
|
@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "voicemeeter-api"
|
name = "voicemeeter-api"
|
||||||
version = "2.6.1"
|
version = "2.7.0"
|
||||||
description = "A Python wrapper for the Voiceemeter API"
|
description = "A Python wrapper for the Voiceemeter API"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Onyx and Iris",email = "code@onyxandiris.online"}
|
{name = "Onyx and Iris",email = "code@onyxandiris.online"}
|
||||||
@ -16,7 +16,7 @@ dependencies = [
|
|||||||
packages = [{ include = "voicemeeterlib" }]
|
packages = [{ include = "voicemeeterlib" }]
|
||||||
|
|
||||||
[tool.poetry.requires-plugins]
|
[tool.poetry.requires-plugins]
|
||||||
poethepoet = "^0.32.1"
|
poethepoet = "^0.35.0"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pytest = "^8.3.4"
|
pytest = "^8.3.4"
|
||||||
@ -37,6 +37,7 @@ levels.script = "scripts:ex_levels"
|
|||||||
midi.script = "scripts:ex_midi"
|
midi.script = "scripts:ex_midi"
|
||||||
obs.script = "scripts:ex_obs"
|
obs.script = "scripts:ex_obs"
|
||||||
observer.script = "scripts:ex_observer"
|
observer.script = "scripts:ex_observer"
|
||||||
|
eqedit.script = "scripts:ex_eqedit"
|
||||||
test-basic.script = "scripts:test_basic"
|
test-basic.script = "scripts:test_basic"
|
||||||
test-banana.script = "scripts:test_banana"
|
test-banana.script = "scripts:test_banana"
|
||||||
test-potato.script = "scripts:test_potato"
|
test-potato.script = "scripts:test_potato"
|
||||||
|
@ -37,6 +37,11 @@ def ex_observer():
|
|||||||
subprocess.run([sys.executable, str(scriptpath)])
|
subprocess.run([sys.executable, str(scriptpath)])
|
||||||
|
|
||||||
|
|
||||||
|
def ex_eqedit():
|
||||||
|
scriptpath = Path.cwd() / 'examples' / 'eq_edit' / '.'
|
||||||
|
subprocess.run([sys.executable, str(scriptpath)])
|
||||||
|
|
||||||
|
|
||||||
def test_basic():
|
def test_basic():
|
||||||
subprocess.run(['tox'], env=os.environ.copy() | {'KIND': 'basic'})
|
subprocess.run(['tox'], env=os.environ.copy() | {'KIND': 'basic'})
|
||||||
|
|
||||||
|
@ -88,6 +88,25 @@ class Bus(IRemote):
|
|||||||
|
|
||||||
|
|
||||||
class BusEQ(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
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f'Bus[{self.index}].eq'
|
return f'Bus[{self.index}].eq'
|
||||||
@ -109,6 +128,86 @@ class BusEQ(IRemote):
|
|||||||
self.setter('ab', 1 if val else 0)
|
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):
|
class PhysicalBus(Bus):
|
||||||
@classmethod
|
@classmethod
|
||||||
def make(cls, remote, i, kind):
|
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),
|
'levels': BusLevel(remote, i),
|
||||||
'mode': BUSMODEMIXIN_cls(remote, i),
|
'mode': BUSMODEMIXIN_cls(remote, i),
|
||||||
'eq': BusEQ(remote, i),
|
'eq': BusEQ.make(remote, i),
|
||||||
},
|
},
|
||||||
)(remote, i)
|
)(remote, i)
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ class KindMapClass(metaclass=SingletonType):
|
|||||||
asio: tuple
|
asio: tuple
|
||||||
insert: int
|
insert: int
|
||||||
composite: int
|
composite: int
|
||||||
|
channels: int
|
||||||
|
cells: int
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def phys_in(self) -> int:
|
def phys_in(self) -> int:
|
||||||
@ -76,6 +78,8 @@ class BasicMap(KindMapClass):
|
|||||||
asio: tuple = (0, 0)
|
asio: tuple = (0, 0)
|
||||||
insert: int = 0
|
insert: int = 0
|
||||||
composite: int = 0
|
composite: int = 0
|
||||||
|
channels: int = 0
|
||||||
|
cells: int = 0
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -86,6 +90,8 @@ class BananaMap(KindMapClass):
|
|||||||
asio: tuple = (6, 8)
|
asio: tuple = (6, 8)
|
||||||
insert: int = 22
|
insert: int = 22
|
||||||
composite: int = 8
|
composite: int = 8
|
||||||
|
channels: int = 8
|
||||||
|
cells: int = 6
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -96,6 +102,8 @@ class PotatoMap(KindMapClass):
|
|||||||
asio: tuple = (10, 8)
|
asio: tuple = (10, 8)
|
||||||
insert: int = 34
|
insert: int = 34
|
||||||
composite: int = 8
|
composite: int = 8
|
||||||
|
channels: int = 8
|
||||||
|
cells: int = 6
|
||||||
|
|
||||||
|
|
||||||
def kind_factory(kind_id):
|
def kind_factory(kind_id):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user