Radiant Voices lets you use JavaScript or Python to create, read, modify, and write SunVox files.
We are working on a Markdown version of our README.
Please refer to the Radiant Voices docs for complete documentation.
Work with SunVox file format tools (create, modify, read, write)
Home Page: https://radiant-voices.rtfd.io/
License: Other
Radiant Voices lets you use JavaScript or Python to create, read, modify, and write SunVox files.
We are working on a Markdown version of our README.
Please refer to the Radiant Voices docs for complete documentation.
Hi,
How do u detach a module from a project and attach it to another one ? Does it need to be explicitly detached / cloned to remove the original module index ? I get the following -
File "/home/justin/work/seavox/lib/python3.6/site-packages/rv/project.py", line 70, in attach_module
module.index = self.module_index(module)
AttributeError: 'str' object attribute 'index' is read-only
so wondering if there is any support for such a thing.
Thx!
Reported by Telegram user.
When loading, this traceback occurs:
In [1]: from rv.api import read_sunvox_file
In [2]: f = read_sunvox_file("test1.sunvox")
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-2-f2f5532262e8> in <module>
----> 1 f = read_sunvox_file("test1.sunvox")
~/proj/sv/voices/src/python/rv/readers/reader.py in read_sunvox_file(file_or_name)
25 try:
26 reader = InitialReader(file_or_name)
---> 27 return reader.object
28 finally:
29 if close:
~/proj/sv/voices/src/python/rv/readers/reader.py in object(self)
41 def object(self):
42 if self._object is None:
---> 43 self.process_chunks()
44 return self._object
45
~/proj/sv/voices/src/python/rv/readers/reader.py in process_chunks(self)
60 if callable(method):
61 log.debug(_F("-> {}.{}", *log_args))
---> 62 method(data)
63 else:
64 log.warning(_F("no {}.{} method", *log_args))
~/proj/sv/voices/src/python/rv/readers/initial.py in process_SVOX(self, _)
6 class InitialReader(Reader):
7 def process_SVOX(self, _):
----> 8 self.object = SunVoxReader(self.f).object
9
10 def process_SSYN(self, _):
~/proj/sv/voices/src/python/rv/readers/reader.py in object(self)
41 def object(self):
42 if self._object is None:
---> 43 self.process_chunks()
44 return self._object
45
~/proj/sv/voices/src/python/rv/readers/sunvox.py in process_chunks(self)
18 self.object = Project()
19 self.object.modules.clear()
---> 20 super().process_chunks()
21
22 def process_VERS(self, data):
~/proj/sv/voices/src/python/rv/readers/reader.py in process_chunks(self)
63 else:
64 log.warning(_F("no {}.{} method", *log_args))
---> 65 self.process_end_of_file()
66 except ReaderFinished:
67 pass
~/proj/sv/voices/src/python/rv/readers/sunvox.py in process_end_of_file(self)
130 for in_link_idx, in_link in enumerate(in_links):
131 out_link_idx = in_link_slots[in_link_idx]
--> 132 src_mod = self.object.modules[in_link]
133 if not src_mod:
134 raise RuntimeError()
IndexError: list index out of range
Hi,
So I created a simple example to test module indexation, specifically within patterns.
I can see that within the modules, indexation starts at zero - Output has index zero by default. Let's say I add a DrumSynth as in the example below - it will have an index of one, as printed out from the example below.
But let's say I now add a simple pattern containing some notes for this module to play - to make it work, the notes seem to require a module index of two, which seems to indicate that note module indexation starts at one rather than zero - is that correct ? If so why ?
Makes it very confusing if you are trying to synchronise creation of new modules and notes to play those modules
[suggest u test the example below]
if __name__=="__main__":
from rv.api import Project
proj=Project()
from rv.modules.drumsynth import DrumSynth
drum=DrumSynth()
proj.attach_module(drum)
print ("module indexes: %s" % [mod.index
for mod in proj.modules]) # module indexation starts at zero
proj.connect(proj.modules[1],
proj.modules[0])
from rv.pattern import Pattern
pat=Pattern(lines=8, tracks=1)
from rv.note import Note
def notefn(track, i, j):
if 0==i%4:
return Note(note=1,
module=2) # eh ?? pattern module refs start at two ??
else:
return Note()
pat.set_via_fn(notefn)
proj.patterns.append(pat)
from rv.lib.iff import write_chunk
with open("tmp/hello_world.sunvox", 'wb') as f:
for name, value in proj.chunks():
if not name:
continue
write_chunk(f, name, value)
A TODO list to track the addition of SunVox 1.9.6-related changes to the radiant-voices 1.0.0-dev branch.
…/sunvox/examples$ python -m rv.tools.player nt\ -\ Berlin\ FM.sunvox
Traceback (most recent call last):
File "/Users/gldnspud/proj/sv/voices/src/python/rv/modules/module.py", line 337, in set_raw
value = t(from_raw_value(raw_value))
File "/Users/gldnspud/proj/sv/voices/src/python/rv/controller.py", line 113, in __call__
self.validate(value)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/controller.py", line 137, in validate
raise e
rv.errors.RangeValidationError: (300, 0, 256)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/gldnspud/.asdf/installs/python/3.9.2/lib/python3.9/runpy.py", line 197, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/Users/gldnspud/.asdf/installs/python/3.9.2/lib/python3.9/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/tools/player.py", line 104, in <module>
sys.exit(main())
File "/Users/gldnspud/proj/sv/voices/src/python/rv/tools/player.py", line 59, in main
obj = read_sunvox_file(filename)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 27, in read_sunvox_file
return reader.object
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 43, in object
self.process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 62, in process_chunks
method(data)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/initial.py", line 8, in process_SVOX
self.object = SunVoxReader(self.f).object
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 43, in object
self.process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/sunvox.py", line 20, in process_chunks
super().process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 62, in process_chunks
method(data)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/sunvox.py", line 109, in process_SFFF
reader.process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/module.py", line 26, in process_chunks
super().process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 62, in process_chunks
method(data)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/module.py", line 128, in process_CHNM
self._load_last_chunk()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/module.py", line 120, in _load_last_chunk
self.object.load_chunk(self._current_chunk)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/modules/metamodule.py", line 275, in load_chunk
self.load_project(chunk)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/modules/metamodule.py", line 284, in load_project
self.project = read_sunvox_file(BytesIO(chunk.chdt))
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 27, in read_sunvox_file
return reader.object
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 43, in object
self.process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 62, in process_chunks
method(data)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/initial.py", line 8, in process_SVOX
self.object = SunVoxReader(self.f).object
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 43, in object
self.process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/sunvox.py", line 20, in process_chunks
super().process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 62, in process_chunks
method(data)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/sunvox.py", line 109, in process_SFFF
reader.process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/module.py", line 26, in process_chunks
super().process_chunks()
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/reader.py", line 62, in process_chunks
method(data)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/readers/module.py", line 154, in process_SEND
self.object.set_raw(controller_name, raw_value)
File "/Users/gldnspud/proj/sv/voices/src/python/rv/modules/module.py", line 340, in set_raw
raise ControllerValueError(
rv.errors.ControllerValueError: 0(MetaModule).user_defined_5=300 is not within [0, 256]
By default, all newly-created modules have the same position. This makes for a cumbersome experience opening rv-generated files using SunVox.
Add a method layout()
that uses the pygraphviz package to determine coordinates, then map them to the space used by SunVox.
I have a simple script which "round trips" a file, just to check input/output serialisation is working -
from rv.lib.iff import write_chunk
def dump_project(project, filename):
with open(filename, 'wb') as f:
for name, value in project.chunks():
if not name:
continue
write_chunk(f, name, value)
if __name__=="__main__":
try:
import sys, os
if len(sys.argv) < 2:
raise RuntimeError("Please enter filename")
filename=sys.argv[1]
if not os.path.exists(filename):
raise RuntimeError("File does not exist")
if not filename.endswith(".sunvox"):
raise RuntimeError("File must be a .sunvox file")
from rv.readers.reader import read_sunvox_file
proj=read_sunvox_file(filename)
destfilename="tmp/%s" % filename.split("/")[-1]
dump_project(proj, destfilename)
except RuntimeError as error:
print ("Error: %s" % str(error))
Works the vast majority of the time , but fails occasionally -
justin@justin-XPS-13-9360:~/work/$ python dev/roundtrip.py ~/packages/sunvox/examples/NightRadio\ -\ Mechanical\ Heart.sunvox
Traceback (most recent call last):
File "dev/roundtrip.py", line 23, in <module>
dump_project(proj, destfilename)
File "dev/roundtrip.py", line 5, in dump_project
for name, value in project.chunks():
File "/home/justin/work/seavox/lib/python3.6/site-packages/rv/project.py", line 155, in chunks
for chunk in module.specialized_iff_chunks():
File "/home/justin/work/seavox/lib/python3.6/site-packages/rv/modules/sampler.py", line 325, in specialized_iff_chunks
for chunk in iter:
File "/home/justin/work/seavox/lib/python3.6/site-packages/rv/modules/sampler.py", line 165, in chunks
data += pack("<HH", x, y)
struct.error: ushort format requires 0 <= number <= (0x7fff * 2 + 1)
Am pretty sure this is something to do with the samples in there; if I clone modules without sample data and try and serialise that, it works fine.
Also to be clear it's not every sample; just something to do with the samples in this particular track
I wanted to push a feature branch and issue a pull request, but got the following -
radiant-voices) justin@justin-XPS-13-9360:~/work/radiant-voices$ git push origin jhw-writers
ERROR: Permission to metrasynth/radiant-voices.git denied to jhw.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
Would like to collaborate, not done much Github collaboration before, are feature branches + pull requests the way to go ? If so could I get the correct permissions ?
Thx!
After following install instructions, when running python helloworld.py I'm getting this error:
Traceback (most recent call last):
File "helloworld.py", line 8, in
from rv.modules import Fm
ImportError: No module named rv.modules
Any help appreciated, thanks
There are some inconsistencies in the way we quantize vs. the way SunVox quantizes.
For example, for this test case, we calculate a value of 128. This makes sense since there are 61 quantization steps, and 16384 is exactly in the middle of the range [0, 32768].
However, SunVox calculates 123. It only calculates 128 when the value becomes 16385.
# gain qsteps smin smax dmin dmax value expected
(256, 61, 0, 32768, 0, 256, 16384, 123),
This will require inspecting the module connections in the project to see what MultiCtl is hooked up to; then calculating the correct value based on the MultiCtl mappings; then updating the value of the mapped destination controller.
Hey @gldnspud,
Have been working with radiant-voices
again, sorry for lack of communication. Everything working fine, but today did a fresh install of RV under virtualenv/python3.6/pip/requirements.txt, and got the following when running one of my normal scripts -
Traceback (most recent call last):
File "dev/drumsynth.py", line 109, in <module>
from rv.api import Project
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/rv/api.py", line 1, in <module>
import rv.modules
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/rv/modules/__init__.py", line 47, in <module>
from .metamodule import MetaModule
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/rv/modules/metamodule.py", line 15, in <module>
from rv.project import Project
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/rv/project.py", line 10, in <module>
import networkx as nx
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/networkx/__init__.py", line 98, in <module>
import networkx.utils
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/networkx/utils/__init__.py", line 2, in <module>
from networkx.utils.decorators import *
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/networkx/utils/decorators.py", line 14, in <module>
from decorator import decorator
File "/home/justin/work/sv_demo/lib/python3.6/site-packages/decorator.py", line 46, in <module>
from inspect import getfullargspec
ImportError: cannot import name 'getfullargspec'
Solved it by commenting out import networkx as nx
in project.py
; but thought u should know as maybe a dependency has changed somewhere
Rgds -
Hi,
Trying to give visible names to the different patterns in my timeline.
I see the Pattern
class has a name
field, and when I set this field the timeline display seems to register this, but -
sometimes the names aren't shown at all
when they are shown, it seems that it's only ever the first character that is shown
Is this a bug or am I doing something wrong ?
Best :)
(PS keep up the good work on RV, am having a lot of fun with it recently, particularly with the sampler, thinking about releasing some RV- based tracks to the Sunvox community over the medium term)
from rv.api import Project as RVProject
from rv.pattern import Pattern as RVPattern
from random import Random
import os
def random_color(q, offset=64, contrast=64, n=16):
def random_color(q, offset):
return [int(offset+q.random()*(255-offset))
for i in range(3)]
for i in range(n):
color=random_color(q, offset)
if (max(color)-min(color)) > contrast:
return color
return [127 for i in range(3)]
if __name__=="__main__":
proj=RVProject()
q=Random()
proj.patterns=[RVPattern(lines=64,
tracks=1,
x=i*64,
y_size=64,
fg_color=(255, 255, 255),
bg_color=random_color(q),
name="%i" % (i+1))
for i in range(16)]
if not os.path.exists("tmp"):
os.mkdir("tmp")
with open("tmp/name-demo.sunvox", 'wb') as f:
proj.write_to(f)
The only use of slugify
is to create Python-compatible aliases for labeled user-defined controllers on MetaModules.
For example, if you had a label called "Cutoff Frequency" then it would alias that controller as u_cutoff_frequency
.
The reason I thought this would be needed is because slugify will do a transformation of non-Latin to Latin characters. For example, "частота среза" would be changed to u_chastota_sreza
.
However, this is not necessary. u_частота_среза
is a valid identifier in Python.
Hi, is this project still live ? Am interested in a) loading/parsing a .sunvox file into memory b) making some programmatic changes to the structure c) saving the results back to file in native .sunvox form. Is that possible ? (guessing so, looking for docs). Also is your .sunvox parser compete ? Any know bits missing ? Thanks in advance.
Each module type has some aspects that could be documented, and also selectively enforced via warnings or exceptions:
@jhw since we're both becoming active with this project again, I want to make sure I can test against the environment you're using. Can you share which release of Ubuntu you're running, so I can set up a VM?
Hi, is there any functionality in here for parsing the structure of the timeline ? Obviously I can get all the patterns, but trying to find which patterns are played simultaneously during the timeline - thx
This can be used to offer both a mutable and an immutable version of the API in Python.
It can also be used to construct the JavaScript version: https://github.com/metrasynth/radiant-voices-js/issues/1
JS version update is not needed.
Represent a container (e.g. project or synth) and all data within using persistent immutable data structures (provided by Pyrsistent).
This will enable low-cost undo/redo operations.
Keep the current imperative-style API.
Introduce a transaction wrapper, e.g.:
with project.transaction() as t:
project.modules[1].volume += 24
undo_stack.append(t.prev)
with open('.recovery.radvox', 'wb') as f:
t.current.write_to(f)
The JS version correctly handles SLNK/SLnK/etc chunks for incoming and outgoing link ordering.
Port the test coverage for this to the Python version, and fix the Python version to correctly handle link ordering.
Reported from Telegram user:
https://github.com/metrasynth/radiant-voices/blob/master/src/python/rv/modules/base/delay.py
lines 32 & 33since we're able to specify delay in ms/hz (up to 8192),
line 3 will generate an exception due to delay > 256 units
Hi,
I have RV installed fine using virtualenv, having recently upgraded from Python2.7 to Python3.5; however when I try and run a hello.py
example I get the following syntax error. Is easily resolved by commenting out the line in the source code, but why am I getting this (I am not 100% up to speed with Python3.5 features/syntax)
Many thanks
(hello_sunvox) justin@justin-XPS-13-9360:~/work/hello_sunvox$ python3 drumsynth/hello.py 32 14
Traceback (most recent call last):
File "drumsynth/hello.py", line 69, in <module>
project=read_project("blank.sunvox")
File "/home/justin/work/hello_sunvox/drumsynth/sunvox.py", line 3, in read_project
return read_sunvox_file(open(filename, 'rb'))
File "/home/justin/work/hello_sunvox/lib/python3.5/site-packages/rv/readers/reader.py", line 11, in read_sunvox_file
from rv.readers.initial import InitialReader
File "/home/justin/work/hello_sunvox/lib/python3.5/site-packages/rv/readers/initial.py", line 2, in <module>
from rv.readers.sunsynth import SunSynthReader
File "/home/justin/work/hello_sunvox/lib/python3.5/site-packages/rv/readers/sunsynth.py", line 3, in <module>
from rv.readers.module import ModuleReader
File "/home/justin/work/hello_sunvox/lib/python3.5/site-packages/rv/readers/module.py", line 7, in <module>
from rv.modules import MODULE_CLASSES, Chunk, Module
File "/home/justin/work/hello_sunvox/lib/python3.5/site-packages/rv/modules/__init__.py", line 47, in <module>
from .metamodule import MetaModule
File "/home/justin/work/hello_sunvox/lib/python3.5/site-packages/rv/modules/metamodule.py", line 28
s = f'_{s}'
^
Hi @gldnspd,
I have two simple Sunvox files, ostensibly the same, but one I created via the Sunvox UI (drum_synth_ui.sunvox
) and one I created using RV (drum_synth_rv.sunvox
).
I say ostensibly the same because the file sizes are slightly different .. the problem I have is that if I load drum_synth_rv.sunvox
in the UI and click on the DrumSynth, Sunvox segv's and crashes, whilst drum_synth_ui.sunvox
is fine for the same action. Trying to figure out what drum_synth_rv.sunvox
is missing / what is causing Sunvox to crash.
I also attach the script I used to create drum_synth_rv.sunvox
(python beats.py 1
)
Thx!
Hi,
I want to load a series of samples into a sampler and assign them to different notes.
I can load and assign the samples fie, but sunvox wants to give each a "base note" (default seems to be C5) which it then uses for pitch shifting.
This means that any sample not assigned to C5 gets pitch shifted, which I don't want.
I can see the base note in the sunvox UI, but don't know how to change in via RV.
Is there a way ?
Thx!
Hi @gldnspud , this is great - I like sunvox a lot but find the sequencer/pattern editor a bit limited and this definitely offers a window into new possibilities.
especially:
Alternative DAW interfaces
The docs don't make it clear how to do common things, such as writing a Project
or Synth
to a file using write_to
, or playing back projects using metrasynth/sunvox-dll-python.
The API reference at https://radiant-voices.readthedocs.io/en/latest/api-reference/index.html also does not include information about classes like Project
and Synth
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.