def test_note_belongs_to_project_of_pattern(): empty_pattern = Pattern(tracks=1, lines=4) note = empty_pattern.data[0][0] assert note.project is None project = Project() project.attach_pattern(empty_pattern) assert note.project is project
def test_connection_shorthand(): p = Project() amp = p.new_module(m.Amplifier) gen = p.new_module(m.Generator) gen >> amp p.output << amp assert gen.index in p.module_connections[amp.index] assert amp.index in p.module_connections[p.output.index]
def hello_world(): project = Project() fm = project.new_module(m.Fm, m_feedback=42) project.connect(fm, project.output) init(None, 44100, 2, 0) with Slot(project) as slot: slot.send_event(0, 42, 32, fm, 0, 0) print("Press Enter to close") input() slot.stop() deinit()
def test_pattern_cannot_be_attached_to_multiple_projects(): empty_pattern = Pattern(tracks=1, lines=4) project = Project() project.attach_pattern(empty_pattern) project2 = Project() with pytest.raises(PatternOwnershipError): project2.attach_pattern(empty_pattern)
def project_from_timeline(timeline: Timeline) -> Project: """Render a Timeline to a SunVox project.""" project = Project() # 120 BPM, 48 ticks per quarter note project.initial_bpm = 120 * 2 project.initial_tpl = 1 voice_modules = dict((voice, voice.sunvox_module()) for voice in set( part.voice for part in timeline.parts)) modules = list(voice_modules.values()) project += modules project.output << modules for placed_phrase in timeline: p = placed_phrase.phrase ticks = p.ticks for i, part in enumerate(p.parts): pattern = Pattern( tracks=16, x=placed_phrase.x, y=placed_phrase.y + i * 10, lines=ticks, ) project += pattern reserved = set() module = voice_modules[part.voice] mm = module.index + 1 for placed_note in p: if placed_note.part is part: start_tick = p.placed_note_on_tick(placed_note) stop_tick = p.placed_note_off_tick(placed_note) for nn in placed_note.notes: track = 0 while (start_tick, track) in reserved: track += 1 assert track < 16, \ f'Track overflow at {start_tick}' cell = pattern.data[start_tick][track] if nn.pitch.sp_value is not None: cell.note = SET_PITCH cell.val = nn.pitch.sp_value else: cell.note = nn.pitch.sunvox_note cell.module = mm for tick in range(start_tick, stop_tick + 1): if tick < ticks: reserved.add((tick, track)) if stop_tick < ticks: cell = pattern.data[stop_tick][track] cell.note = NOTE_OFF return project
def test_reflect(): p = Project() amp1 = p.new_module(m.Amplifier) amp2 = p.new_module(m.Amplifier) mc = p.new_module(m.MultiCtl) mc >> amp1 mc >> amp2 mc.mappings.values[0].min = 32768 mc.mappings.values[0].max = 0 mc.mappings.values[0].controller = amp1.controllers["volume"].number mc.mappings.values[1].controller = amp2.controllers["volume"].number amp1.volume = 0 mc.reflect(0) assert mc.value == 32768 assert amp2.volume == 1024
def test_propagation_multisynth_transpose_rangelimit(): p = Project() ms1 = p.new_module(m.MultiSynth) mc = p.new_module(m.MultiCtl) mc >> ms1 mapping = mc.mappings.values[0] mapping.controller = ms1.controllers["transpose"].number mapping.min = 128 mapping.max = 144 mapping.gain = 256 + int(256 / 17) mc.value = 0 assert ms1.transpose == 0 mc.value = 16384 assert ms1.transpose == 8 mc.value = 32768 assert ms1.transpose == 16
def dump(props, patch, output_dir, dirname): patch_dir = f"{output_dir}/{dirname}/{patch['name']}" os.makedirs(patch_dir, exist_ok=True) proj = Project() proj.initial_bpm = props["bpm"] proj.initial_tpl = props["tpl"] layout = module_layout(len(patch["modules"])) for i, mod in enumerate(reversed(patch["modules"][:-1])): mod.x, mod.y = layout[i] proj.attach_module(mod) for i in range(len(proj.modules) - 1): proj.connect(proj.modules[i + 1], proj.modules[i]) proj.patterns.append(patch["pattern"]) destfilename = f"{patch_dir}/{patch['x']}.sunvox" with open(destfilename, "wb") as f: proj.write_to(f)
def test_note_mod_property(): project = Project() pattern = Pattern(tracks=1, lines=4) project.attach_pattern(pattern) note = pattern.data[0][0] assert note.module == 0 assert note.module_index is None assert note.mod is None mod: m.Generator = project.new_module(m.Generator) note.mod = mod assert note.mod is mod assert note.module_index == mod.index assert note.module == mod.index + 1 note.module = 5 assert note.mod is None assert note.module_index == 4
def test_propagation_amp_volume(): p = Project() amp1 = p.new_module(m.Amplifier) amp2 = p.new_module(m.Amplifier) mc = p.new_module(m.MultiCtl) mc >> amp1 mc >> amp2 mc.mappings.values[0].min = 32768 mc.mappings.values[0].max = 0 mc.mappings.values[0].controller = amp1.controllers["volume"].number mc.mappings.values[1].controller = amp2.controllers["volume"].number mc.value = 0 assert amp1.volume == 1024 assert amp2.volume == 0 mc.value = 16384 assert amp1.volume == 512 assert amp2.volume == 512 mc.value = 32768 assert amp1.volume == 0 assert amp2.volume == 1024
def test_propagation_multisynth_transpose(): p = Project() ms1 = p.new_module(m.MultiSynth) ms2 = p.new_module(m.MultiSynth) mc = p.new_module(m.MultiCtl) mc >> ms1 mc >> ms2 mc.mappings.values[0].min = 0 mc.mappings.values[0].max = 256 mc.mappings.values[0].controller = ms1.controllers["transpose"].number mc.mappings.values[1].min = 256 mc.mappings.values[1].max = 0 mc.mappings.values[1].controller = ms2.controllers["transpose"].number mc.value = 0 assert ms1.transpose == -128 assert ms2.transpose == 128 mc.value = 16384 assert ms1.transpose == 0 assert ms2.transpose == 0 mc.value = 32768 assert ms1.transpose == 128 assert ms2.transpose == -128
def test_layout(): p = Project() out = p.output gen1 = p.new_module(m.Generator) gen2 = p.new_module(m.Generator) amp = p.new_module(m.Amplifier) amp << [gen1, gen2] amp >> out assert [gen1.x, gen2.x, amp.x, out.x].count(512) == 4 assert [gen1.y, gen2.y, amp.y, out.y].count(512) == 4 p.layout() assert [gen1.x, gen2.x, amp.x, out.x].count(512) != 4 assert [gen1.y, gen2.y, amp.y, out.y].count(512) != 4
def test_pattern_project_is_set_after_attaching(): empty_pattern = Pattern(tracks=1, lines=4) project = Project() project.attach_pattern(empty_pattern) assert empty_pattern.project is project