Skip to content

Commit

Permalink
v0.02
Browse files Browse the repository at this point in the history
  • Loading branch information
jgillmanjr committed Feb 22, 2021
1 parent 0e3112e commit 464830d
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 176 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
config.json
config.json
__pycache__
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Changelog
=========
## v0.02
* Skins now supported
* Refactor
* Created module
* Abstract `VitalObject` class created

## v0.01
* The first version.
Expand Down
183 changes: 8 additions & 175 deletions bankify.py
Original file line number Diff line number Diff line change
@@ -1,177 +1,10 @@
from __future__ import annotations
from pathlib import Path
from typing import Dict, Iterable, Optional, Tuple, Union
from zipfile import ZipFile, ZIP_DEFLATED
from typing import Dict, Optional, Tuple
from vital import Bank, VitalObject
import json


class Lfo:
"""
A Vital LFO
"""
EXTENSION = 'vitallfo'
DIR = 'LFOs'

@classmethod
def from_file(cls, filepath: Path) -> Lfo:
"""
Create an LFO from a file
:param filepath:
:return:
"""
with filepath.open('r') as f:
return cls(data=json.load(f), filepath=filepath)

def __str__(self):
return self.name

def __repr__(self):
return str(self)

def __init__(self, data: dict, filepath: Optional[Path]):
self._data = data
self.original_path = filepath
self.name = self._data['name']

def rename(self, new_name: str):
"""
Rename
:param new_name:
:return:
"""
self._data['name'] = new_name
self.name = self._data['name']


class Preset:
"""
A Vital Preset
"""
EXTENSION = 'vital'
DIR = 'Presets'

@classmethod
def from_file(cls, filepath: Path) -> Preset:
"""
Create an Preset from a file
:param filepath:
:return:
"""
with filepath.open('r') as f:
return cls(data=json.load(f), filepath=filepath)

def __str__(self):
return self.name

def __repr__(self):
return str(self)

def __init__(self, data: dict, filepath: Optional[Path], **kwargs):
self._data = data
self.original_path = filepath

if filepath is not None: # Differs because it appears the name comes from the file
self.name = filepath.stem
else:
self.name = kwargs['name']

def rename(self, new_name: str):
"""
Rename
:param new_name:
:return:
"""
self.name = new_name


class Wavetable:
"""
A Vital Wavetable
"""
EXTENSION = 'vitaltable'
DIR = 'Wavetables'

@classmethod
def from_file(cls, filepath: Path) -> Wavetable:
"""
Create an LFO from a file
:param filepath:
:return:
"""
with filepath.open('r') as f:
return cls(data=json.load(f), filepath=filepath)

def __str__(self):
return self.name

def __repr__(self):
return str(self)

def __init__(self, data: dict, filepath: Optional[Path]):
self._data = data
self.original_path = filepath
self.name = self._data['name']

def rename(self, new_name: str):
"""
Rename
:param new_name:
:return:
"""
self._data['name'] = new_name
self.name = self._data['name']


class Bank:
"""
Represents a bank comprised of LFOs, Presets, and Wavetables
"""
EXTENSION = 'vitalbank'

def __str__(self):
return self.name

def __repr__(self):
return str(self)

def __init__(self, bank_name: str, lfos: Optional[Iterable[Lfo]], presets: Optional[Iterable[Preset]],
wavetables: Optional[Iterable[Wavetable]]):
self.name = bank_name
self.elements = {
Lfo: {lfo.name: lfo for lfo in lfos} if lfos is not None else list(),
Preset: {preset.name: preset for preset in presets} if presets is not None else list(),
Wavetable: {wavetable.name: wavetable for wavetable in wavetables} if wavetables is not None else list(),
}

def write_file(self, filedir: Path):
"""
Write the bank file to disk
:param filedir: The directory to write to
:return:
"""
filepath = filedir.joinpath(f'{self.name}.{Bank.EXTENSION}')

bankfile = ZipFile(file=filepath, mode='w', compression=ZIP_DEFLATED, compresslevel=9)

for ct, ctd in self.elements.items():
zdir = f'{self.name}/{ct.DIR}'
for i in ctd:
zfile = f'{zdir}/{i.name}.{ct.EXTENSION}'
bankfile.writestr(zinfo_or_arcname=zfile, data=json.dumps(i._data))

bankfile.close()


TYPE_DATA = {
Lfo: ('LFOs', 'vitallfo'),
Preset: ('Presets', 'vital'),
Wavetable: ('Wavetables', 'vitaltable'),
}

CONFIG_FILE = Path('config.json')
with CONFIG_FILE.open('r') as config_file:
CONFIG = json.load(config_file)
Expand All @@ -181,7 +14,7 @@ def write_file(self, filedir: Path):
BANK_DELIM = CONFIG['delimiter']


def is_bank_obj(delim: str, vobj: Union[Lfo, Preset, Wavetable]) -> Optional[Union[Lfo, Preset, Wavetable]]:
def is_bank_obj(delim: str, vobj: VitalObject) -> Optional[VitalObject]:
"""
If an object name contains the delmiter and matches the pattern for belonging in a bank, return it. None otherwise.
First delimiter must be at the beginning
Expand All @@ -200,7 +33,7 @@ def is_bank_obj(delim: str, vobj: Union[Lfo, Preset, Wavetable]) -> Optional[Uni
return None


def bank_comps(delim: str, vobj: Union[Lfo, Preset, Wavetable]) -> Tuple[str, str]:
def bank_comps(delim: str, vobj: VitalObject) -> Tuple[str, str]:
"""
Return the bank name and the name of the object with the bank bits stripped out.
Expand All @@ -223,16 +56,16 @@ def bank_comps(delim: str, vobj: Union[Lfo, Preset, Wavetable]) -> Tuple[str, st
def main():
banks: Dict[str, Bank] = dict()

for clstype, typedata in TYPE_DATA.items():
subdir = USER_DIR.joinpath(typedata[0])
for clstype in VitalObject.__subclasses__():
subdir = USER_DIR.joinpath(clstype.DIR)
for f in subdir.glob('*'):
if not f.is_dir() and f.suffix == f'.{clstype.EXTENSION}':
vobj = clstype.from_file(filepath=f)
if is_bank_obj(delim=BANK_DELIM, vobj=vobj) is not None:
bank_name, obj_name = bank_comps(delim=BANK_DELIM, vobj=vobj)
banks.setdefault(bank_name, Bank(bank_name=bank_name, lfos=None, presets=None, wavetables=None))
banks.setdefault(bank_name, Bank(bank_name=bank_name, vital_objects=None))
vobj.rename(new_name=obj_name) # Set the "clean" name
banks[bank_name].elements[clstype].append(vobj)
banks[bank_name].add_object(vital_object=vobj)

for bank in banks.values():
print(bank.name)
Expand Down
160 changes: 160 additions & 0 deletions vital/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
from __future__ import annotations
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
from typing import Dict, Iterable, Optional, Type
import json


class VitalObject:
"""
A base class for Vital Objects
"""
EXTENSION: str = None
DIR: str = None
name: str = None
original_path: Optional[Path] = None
_data: dict = None

@classmethod
def from_file(cls, filepath: Path) -> VitalObject:
"""
Create an LFO from a file
:param filepath:
:return:
"""
with filepath.open('r') as f:
return cls(data=json.load(f), filepath=filepath)

def __str__(self):
return self.name

def __repr__(self):
return str(self)

def __init__(self, data: dict, filepath: Optional[Path], **kwargs):
self._modified = False
self._data = data
self.original_path = filepath
self.top_level_keys = list(self._data.keys())

if 'name' in self.top_level_keys:
self.name = self._data['name']
elif filepath is not None:
self.name = filepath.stem
elif 'name' in kwargs:
self.name = kwargs['name']
else:
raise Exception('Unable to define name for object.')

def rename(self, new_name: str) -> None:
"""
Rename the object
:param new_name:
:return:
"""
if 'name' in self.top_level_keys:
self._data['name'] = new_name
self.name = new_name
self._modified = True

def return_data(self) -> dict:
"""
Return the raw underlying data
:return:
"""
return self._data

def is_modified(self) -> bool:
"""
Return a boolean indicating if the object has been modified from original creation
:return:
"""
return self._modified


class Lfo(VitalObject):
"""
A Vital LFO
"""
EXTENSION = 'vitallfo'
DIR = 'LFOs'


class Preset(VitalObject):
"""
A Vital Preset
"""
EXTENSION = 'vital'
DIR = 'Presets'


class Skin(VitalObject):
"""
A Vital Skin
"""
EXTENSION = 'vitalskin'
DIR = 'Skins'


class Wavetable(VitalObject):
"""
A Vital Wavetable
"""
EXTENSION = 'vitaltable'
DIR = 'Wavetables'


class Bank:
"""
Represents a bank comprised of various VitalObjects
"""
EXTENSION = 'vitalbank'

def __str__(self):
return self.name

def __repr__(self):
return str(self)

def __init__(self, bank_name: str, vital_objects: Optional[Iterable[VitalObject]]):
self.name = bank_name
self.elements: Dict[Type[VitalObject], Dict[str, VitalObject]] = dict()

if vital_objects is not None:
for vobj in vital_objects:
self.add_object(vobj)

def add_object(self, vital_object: VitalObject) -> None:
"""
Add a VitalObject to the Bank
:param vital_object:
:return:
"""
if not isinstance(vital_object, VitalObject):
raise Exception(f'{type(vital_object).__name__} is not recognized as VitalObject.')

self.elements.setdefault(type(vital_object), dict())
self.elements[type(vital_object)][vital_object.name] = vital_object

def write_file(self, filedir: Path):
"""
Write the bank file to disk
:param filedir: The directory to write to
:return:
"""
filepath = filedir.joinpath(f'{self.name}.{Bank.EXTENSION}')

bankfile = ZipFile(file=filepath, mode='w', compression=ZIP_DEFLATED, compresslevel=9)

for ct, ctd in self.elements.items():
zdir = f'{self.name}/{ct.DIR}'
for i in ctd.values():
zfile = f'{zdir}/{i.name}.{ct.EXTENSION}'
bankfile.writestr(zinfo_or_arcname=zfile, data=json.dumps(i.return_data()))

bankfile.close()

0 comments on commit 464830d

Please sign in to comment.