version 2.0.0 section added to changelog

readme updated to reflect latest changes

test badges updated.

fixes #5
This commit is contained in:
onyx-and-iris 2023-06-23 18:29:41 +01:00
parent f57475daa0
commit 2f9864cf60
6 changed files with 171 additions and 32 deletions

2
.gitignore vendored
View File

@ -131,5 +131,7 @@ dmypy.json
# test/config # test/config
quick.py quick.py
config.toml config.toml
vm-api.log
logging.json
.vscode/ .vscode/

View File

@ -11,6 +11,68 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
- [x] - [x]
## [2.0.0] - 2023-06-19
Where possible I've attempted to make the changes backwards compatible. The breaking changes affect two higher classes, Strip and Bus, as well as the behaviour of events. All other changes are additive or QOL aimed at giving more options to the developer. For example, every low-level CAPI call is now logged and error raised on Exception, you can now register callback functions as well as observer classes, extra examples to demonstrate different use cases etc.
This breaking changes are as follows:
### Changed
- `strip[i].comp` now references StripComp class
- To change the comp knob you should now use the property `strip[i].comp.knob`
- `strip[i].gate` now references StripGate class
- To change the gate knob you should now use the property `strip[i].gate.knob`
- `bus[i].eq` now references BusEQ class
- To set bus[i].{eq,eq_ab} as before you should now use bus[i].eq.on and bus[i].eq.ab
- by default, <strong>NO</strong> events are checked for. This is reflected in factory.FactoryBase defaultkwargs.
- This is a fundamental behaviour change from version 1.0 of the wrapper. It means the following:
- Unless any events are explicitly requested with an event kwarg the event emitter thread will not run automatically.
- Whether using a context manager or not, you can still initiate the event thread manually and request events with the event object.<br>
see `events` example.
There are other non-breaking changes:
### Added
- `strip[i].eq` added to PhysicalStrip
- `strip[i].denoiser` added to PhysicalStrip
- `Strip.Comp`, `Strip.Gate`, `Strip.Denoiser` sections added to README.
- `Events` section in readme updated to reflect changes to events kwargs.
- new comp, gate, denoiser and eq tests added to higher tests.
- `levels` example to demonstrate use of the interface without a context manager.
- `events` example to demonstrate how to interact with event thread/event object.
- `{Remote}.observer` can be used in place of `{Remote}.subject` although subject will still work. Check examples.
- Subject class extended to allow registering/de-registering callback functions (as well as observer classes). See `events` example.
### Changed
- `comp.knob`, `gate.knob`, `denoiser.knob`, `eq.on` added to phys_strip_params in config.TOMLStrBuilder
- The `example.toml` config files have been updated to demonstrate setting new comp, gate and eq settings.
- event kwargs can now be set directly. no need for `subs`. example: `voicemeeterlib.api('banana', midi=True})`
- factorybuilder steps now logged in DEBUG mode.
- now using a producer thread to send events to the updater thread.
- module level loggers implemented (with class loggers as child loggers)
- config.loader now checks `Path.home() / ".config" / "voicemeeter" / kind.name` for configs.
- note. `Path(__file__).parent / "configs" / kind.name,` was removed as a path to check.
### Fixed
- All low level CAPI calls are now wrapped by CBindings.call() which logs any errors raised.
- Dynamic binding of Macrobutton functions from the CAPI.
Should add backwards compatibility with very old versions of the api. See [Issue #4][issue 4].
- factory.request_remote_obj now raises a `VMError` if passed an incorrect kind.
## [1.0.0] - 2023-06-19 ## [1.0.0] - 2023-06-19
No changes to the codebase but it has been stable for several months and should already have been bumped to major version 1.0 No changes to the codebase but it has been stable for several months and should already have been bumped to major version 1.0
@ -298,3 +360,5 @@ I will move this commit to a separate branch in preparation for version 2.0.
- inst module implemented (fetch vm path from registry) - inst module implemented (fetch vm path from registry)
- kind maps implemented as dataclasses - kind maps implemented as dataclasses
- project packaged with poetry and added to pypi. - project packaged with poetry and added to pypi.
[issue 4]: https://github.com/onyx-and-iris/voicemeeter-api-python/issues/4

131
README.md
View File

@ -52,16 +52,18 @@ class ManyThings:
def other_things(self): def other_things(self):
self.vm.bus[3].gain = -6.3 self.vm.bus[3].gain = -6.3
self.vm.bus[4].eq = True self.vm.bus[4].eq.on = True
info = ( info = (
f"bus 3 gain has been set to {self.vm.bus[3].gain}", f"bus 3 gain has been set to {self.vm.bus[3].gain}",
f"bus 4 eq has been set to {self.vm.bus[4].eq}", f"bus 4 eq has been set to {self.vm.bus[4].eq.on}",
) )
print("\n".join(info)) print("\n".join(info))
def main(): def main():
with voicemeeterlib.api(kind_id) as vm: KIND_ID = "banana"
with voicemeeterlib.api(KIND_ID) as vm:
do = ManyThings(vm) do = ManyThings(vm)
do.things() do.things()
do.other_things() do.other_things()
@ -79,16 +81,14 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
kind_id = "banana"
main() main()
``` ```
Otherwise you must remember to call `vm.login()`, `vm.logout()` at the start/end of your code. Otherwise you must remember to call `vm.login()`, `vm.logout()` at the start/end of your code.
## `kind_id` ## `KIND_ID`
Pass the kind of Voicemeeter as an argument. kind_id may be: Pass the kind of Voicemeeter as an argument. KIND_ID may be:
- `basic` - `basic`
- `banana` - `banana`
@ -104,8 +104,6 @@ The following properties are available.
- `solo`: boolean - `solo`: boolean
- `mute`: boolean - `mute`: boolean
- `gain`: float, from -60.0 to 12.0 - `gain`: float, from -60.0 to 12.0
- `comp`: float, from 0.0 to 10.0
- `gate`: float, from 0.0 to 10.0
- `audibility`: float, from 0.0 to 10.0 - `audibility`: float, from 0.0 to 10.0
- `limit`: int, from -40 to 12 - `limit`: int, from -40 to 12
- `A1 - A5`, `B1 - B3`: boolean - `A1 - A5`, `B1 - B3`: boolean
@ -154,7 +152,72 @@ vm.strip[5].appmute("Spotify", True)
vm.strip[5].appgain("Spotify", 0.5) vm.strip[5].appgain("Spotify", 0.5)
``` ```
##### Gainlayers #### Strip.Comp
- `knob`: float, from 0.0 to 10.0
- `gainin`: float, from -24.0 to 24.0
- `ratio`: float, from 1.0 to 8.0
- `threshold`: float, from -40.0 to -3.0
- `attack`: float, from 0.0 to 200.0
- `release`: float, from 0.0 to 5000.0
- `knee`: float, from 0.0 to 1.0
- `gainout`: float, from -24.0 to 24.0
- `makeup`: boolean
example:
```python
print(vm.strip[4].comp.knob)
```
Strip Comp parameters are defined for PhysicalStrips, potato version only.
#### Strip.Gate
- `knob`: float, from 0.0 to 10.0
- `threshold`: float, from -60.0 to -10.0
- `damping`: float, from -60.0 to -10.0
- `bpsidechain`: int, from 100 to 4000
- `attack`: float, from 0.0 to 1000.0
- `hold`: float, from 0.0 to 5000.0
- `release`: float, from 0.0 to 5000.0
example:
```python
vm.strip[2].gate.attack = 300.8
```
Strip Gate parameters are defined for PhysicalStrips, potato version only.
#### Strip.Denoiser
- `knob`: float, from 0.0 to 10.0
example:
```python
vm.strip[0].denoiser.knob = 0.5
```
Strip Denoiser parameters are defined for PhysicalStrips, potato version only.
#### Strip.EQ
The following properties are available.
- `on`: boolean
- `ab`: boolean
example:
```python
vm.strip[0].eq.ab = True
```
Strip EQ parameters are defined for PhysicalStrips, potato version only.
##### Strip.Gainlayers
- `gain`: float, from -60.0 to 12.0 - `gain`: float, from -60.0 to 12.0
@ -166,7 +229,7 @@ vm.strip[3].gainlayer[3].gain = 3.7
Gainlayers are defined for potato version only. Gainlayers are defined for potato version only.
##### Levels ##### Strip.Levels
The following properties are available. The following properties are available.
@ -187,8 +250,6 @@ Level properties will return -200.0 if no audio detected.
The following properties are available. The following properties are available.
- `mono`: boolean - `mono`: boolean
- `eq`: boolean
- `eq_ab`: boolean
- `mute`: boolean - `mute`: boolean
- `sel`: boolean - `sel`: boolean
- `gain`: float, from -60.0 to 12.0 - `gain`: float, from -60.0 to 12.0
@ -208,7 +269,20 @@ print(vm.bus[0].label)
vm.bus[4].mono = True vm.bus[4].mono = True
``` ```
##### Modes ##### Bus.EQ
The following properties are available.
- `on`: boolean
- `ab`: boolean
example:
```python
vm.bus[3].eq.on = True
```
##### Bus.Modes
The following properties are available. The following properties are available.
@ -236,7 +310,7 @@ vm.bus[4].mode.amix = True
print(vm.bus[2].mode.get()) print(vm.bus[2].mode.get())
``` ```
##### Levels ##### Bus.Levels
The following properties are available. The following properties are available.
@ -409,7 +483,7 @@ example:
```python ```python
import voicemeeterlib import voicemeeterlib
with voicemeeterlib.api(kind_id) as vm: with voicemeeterlib.api(KIND_ID) as vm:
for i in range(vm.device.ins): for i in range(vm.device.ins):
print(vm.device.input(i)) print(vm.device.input(i))
``` ```
@ -595,19 +669,19 @@ will load a user config file at configs/banana/example.toml for Voicemeeter Bana
## Events ## Events
Level updates are considered high volume, by default they are NOT listened for. Use subs keyword arg to initialize event updates. By default, NO events are listened for. Use events kwargs to enable specific event types.
example: example:
```python ```python
import voicemeeterlib import voicemeeterlib
# Set updates to occur every 50ms # Set event updates to occur every 50ms
# Listen for level updates but disable midi updates # Listen for level updates only
with voicemeeterlib.api('banana', ratelimit=0.05, subs={"ldirty": True, "midi": False}) as vm: with voicemeeterlib.api('banana', ratelimit=0.05, ldirty=True}) as vm:
... ...
``` ```
#### `vm.subject` #### `vm.observer`
Use the Subject class to register an app as event observer. Use the Subject class to register an app as event observer.
@ -622,7 +696,7 @@ example:
# register an app to receive updates # register an app to receive updates
class App(): class App():
def __init__(self, vm): def __init__(self, vm):
vm.subject.add(self) vm.observer.add(self)
... ...
``` ```
@ -664,17 +738,16 @@ print(vm.event.get())
## Remote class ## Remote class
`voicemeeterlib.api(kind_id: str)` `voicemeeterlib.api(KIND_ID: str)`
You may pass the following optional keyword arguments: You may pass the following optional keyword arguments:
- `sync`: boolean=False, force the getters to wait for dirty parameters to clear. For most cases leave this as False. - `sync`: boolean=False, force the getters to wait for dirty parameters to clear. For most cases leave this as False.
- `ratelimit`: float=0.033, how often to check for updates in ms. - `ratelimit`: float=0.033, how often to check for updates in ms.
- `subs`: dict={"pdirty": True, "mdirty": True, "midi": True, "ldirty": False}, initialize which event updates to listen for. - `pdirty`: boolean=False, parameter updates
- `pdirty`: parameter updates - `mdirty`: boolean=False, macrobutton updates
- `mdirty`: macrobutton updates - `midi`: boolean=False, midi updates
- `midi`: midi updates - `ldirty`: boolean=False, level updates
- `ldirty`: level updates
Access to lower level Getters and Setters are provided with these functions: Access to lower level Getters and Setters are provided with these functions:
@ -705,4 +778,4 @@ pytest -v
### Official Documentation ### Official Documentation
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf) - [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/update-docs/VoicemeeterRemoteAPI.pdf)

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 140"><title>tests: 140</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">140</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">140</text></g></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 139"><title>tests: 139</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">139</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">139</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 116"><title>tests: 116</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">116</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">116</text></g></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 112"><title>tests: 112</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">112</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">112</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 158"><title>tests: 158</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">158</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">158</text></g></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 164"><title>tests: 164</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">164</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">164</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB