Skip to content

Commit

Permalink
fix: closes #150, cleans VSTPluginEvent
Browse files Browse the repository at this point in the history
  • Loading branch information
demberto committed May 24, 2023
1 parent c3b83a9 commit 7e1b108
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 47 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.md
Expand Up @@ -8,6 +8,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.1.1] - 2023-05-24

### Changed

- Refactored `VSTPluginEvent` sub-event handling into `_VSTPluginProp`.
- All `VSTPluginEvent` string sub-events decoded as UTF8.

### Fixed

- `VSTPlugin.name` encoded in UTF8 [#150].

[#150]: https://github.com/demberto/PyFLP/issues/150

## [2.1.0] - 2023-04-18

### Added
Expand Down Expand Up @@ -610,7 +623,7 @@ Same as in 0.1.1

- Changed documentation from Sphinx to MkDocs.
- [FLPInfo](https://github.com/demberto/FLPInfo) is now a separate package.
- [FLPInspect](https://github.com/demberto/FLPInspect) is now a separate package.
- FLPInspect is now a separate package.
- PyFLP now uses [BytesIOEx](https://github.com/demberto/BytesIOEx/) as an external dependency.

### Fixed
Expand Down Expand Up @@ -660,6 +673,7 @@ Same as in 0.1.1
- `flpinfo` doesn't output correctly sometimes due to long strings.
- Extraneous data dumped sometimes by `InsertSlotEvent.Plugin`, why this is caused is not known.

[2.1.1]: https://github.com/demberto/PyFLP/compare/v2.1.0...v2.1.1
[2.1.0]: https://github.com/demberto/PyFLP/compare/v2.0.0...v2.1.0
[2.0.0]: https://github.com/demberto/PyFLP/compare/v2.0.0a7.post0...v2.0.0
[2.0.0a7]: https://github.com/demberto/PyFLP/compare/v2.0.0a6...v2.0.0a7
Expand Down
74 changes: 28 additions & 46 deletions pyflp/plugin.py
Expand Up @@ -232,24 +232,18 @@ class WrapperEvent(StructEventBase):

@enum.unique
class _VSTPluginEventID(ct.EnumBase):
def __new__(cls, id: int, ascii: bool = False):
obj = int.__new__(cls, id)
obj._value_ = id
setattr(obj, "ascii", ascii)
return obj

MIDI = 1
Flags = 2
IO = 30
Inputs = 31
Outputs = 32
PluginInfo = 50
FourCC = (51, True) # Not present for Waveshells & VST3
FourCC = 51 # Not present for Waveshells & VST3
GUID = 52
State = 53
Name = (54, True)
PluginPath = (55, True)
Vendor = (56, True)
Name = 54
PluginPath = 55
Vendor = 56
_57 = 57 # TODO, not present for Waveshells


Expand Down Expand Up @@ -314,6 +308,10 @@ class VSTPluginEvent(StructEventBase):
{
_VSTPluginEventID.MIDI: _MIDIStruct,
_VSTPluginEventID.Flags: _FlagsStruct,
_VSTPluginEventID.FourCC: c.GreedyString("utf8"),
_VSTPluginEventID.Name: c.GreedyString("utf8"), # See #150
_VSTPluginEventID.Vendor: c.GreedyString("utf8"),
_VSTPluginEventID.PluginPath: c.GreedyString("utf8"),
},
default=c.GreedyBytes,
),
Expand All @@ -333,33 +331,6 @@ def __init__(self, id: Any, data: bytearray) -> None:
)
super().__init__(id, data)

def __getitem__(self, key: Any):
if not isinstance(key, _VSTPluginEventID):
raise TypeError("Expected 'key' to be of type _VSTPluginEventID")

for e in self._struct["events"]:
if e["id"] == key:
return e["data"].decode("ascii") if e["id"].ascii else e["data"]
raise AttributeError(f"No event with key {key!r} found")

def __setitem__(self, key: Any, value: Any) -> None:
if not isinstance(key, _VSTPluginEventID):
raise TypeError("Expected 'key' to be of type _VSTPluginEventID")

for e in self._struct["events"]:
if e["id"] == key:
if e["id"].ascii and isinstance(value, str):
try:
value = value.encode("ascii")
except UnicodeEncodeError as exc:
raise ValueError("Strings must have only ASCII data") from exc
e["size"] = len(value)
e["data"] = value

# Errors if any, will be raised here itself, so its
# better not to override __bytes__ for this part
self._data = self.STRUCT.build(self._struct)


@enum.unique
class PluginID(EventEnum):
Expand Down Expand Up @@ -490,13 +461,16 @@ def __init__(self, prop: str | None = None, **kwds: Any) -> None:


class _VSTPluginProp(RWProperty[T], NamedPropMixin):
def __init__(self, id: Any, prop: str | None = None) -> None:
def __init__(self, id: _VSTPluginEventID, prop: str | None = None) -> None:
self._id = id
NamedPropMixin.__init__(self, prop)

def __get__(self, ins: EventModel, _=None) -> T:
value = cast(VSTPluginEvent, ins.events.first(PluginID.Data))[self._id]
return self._get(value)
event = cast(VSTPluginEvent, ins.events.first(PluginID.Data))
for e in event["events"]:
if e["id"] == self._id:
return self._get(e["data"])
raise AttributeError(self._id)

def _get(self, value: Any) -> T:
return cast(T, value if isinstance(value, (str, bytes)) else value[self._prop])
Expand All @@ -505,11 +479,16 @@ def __set__(self, ins: EventModel, value: T) -> None:
self._set(cast(VSTPluginEvent, ins.events.first(PluginID.Data)), value)

def _set(self, event: VSTPluginEvent, value: T) -> None:
event[self._id] = value
for e in event["events"]:
if e["id"] == self._id:
e["data"] = value
break


class _VSTFlagProp(_VSTPluginProp[bool]):
def __init__(self, flag: Any, prop: str = "flags", inverted: bool = False) -> None:
def __init__(
self, flag: _VSTFlags | _VSTFlags2, prop: str = "flags", inverted: bool = False
) -> None:
super().__init__(_VSTPluginEventID.Flags, prop)
self._flag = flag
self._inverted = inverted
Expand All @@ -522,10 +501,13 @@ def _set(self, event: VSTPluginEvent, value: bool) -> None:
if self._inverted:
value = not value

if value:
event[self._id][self._prop] |= value
else:
event[self._id][self._prop] &= ~value
for e in event["events"]:
if e["id"] == self._id:
if value:
e["data"][self._prop] |= value
else:
e["data"][self._prop] &= ~value
break


class PluginIOInfo(EventModel):
Expand Down

0 comments on commit 7e1b108

Please sign in to comment.