Nils
3 years ago
4 changed files with 150 additions and 9 deletions
@ -0,0 +1,110 @@ |
|||||
|
#! /usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
NUMBER_OF_INSTRUMENTS = 240 |
||||
|
""" |
||||
|
2021-11-13 Benchmark: |
||||
|
NumberOfInstruments,StartInSeconds,QuitInSeconds |
||||
|
30, 5, 3 |
||||
|
60, 10, 6 |
||||
|
120, 21, 12 |
||||
|
240, 42, 25 |
||||
|
|
||||
|
Conclusion: Linear time. |
||||
|
""" |
||||
|
|
||||
|
from calfbox import cbox |
||||
|
|
||||
|
import atexit |
||||
|
from datetime import datetime |
||||
|
|
||||
|
#Capture Ctlr+C / SIGINT and let @atexit handle the rest. |
||||
|
import signal |
||||
|
import sys |
||||
|
def signal_handler(sig, frame): |
||||
|
sys.exit(0) #atexit will trigger |
||||
|
signal.signal(signal.SIGINT, signal_handler) |
||||
|
|
||||
|
|
||||
|
def cmd_dumper(cmd, fb, args): |
||||
|
#print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) |
||||
|
pass |
||||
|
|
||||
|
def stopSession(): |
||||
|
"""This got registered with atexit in the nsm new or open callback above. |
||||
|
will handle all python exceptions, but not segfaults of C modules. """ |
||||
|
print() |
||||
|
print("Starting Quit through @atexit, stopSession") |
||||
|
starttime = datetime.now() |
||||
|
#Don't do that. We are just a client. |
||||
|
#cbox.Transport.stop() |
||||
|
#print("@atexit: Calfbox Transport stopped ") |
||||
|
cbox.stop_audio() |
||||
|
print("@atexit: Calfbox Audio stopped ") |
||||
|
cbox.shutdown_engine() |
||||
|
print("@atexit: Calfbox Engine shutdown ") |
||||
|
endtime = datetime.now() - starttime |
||||
|
print (f"Shutdown took {endtime.seconds} seconds for {NUMBER_OF_INSTRUMENTS} instruments") |
||||
|
|
||||
|
cbox.init_engine() |
||||
|
cbox.start_audio(cmd_dumper) |
||||
|
atexit.register(stopSession) #this will handle all python exceptions, but not segfaults of C modules. |
||||
|
|
||||
|
scenes = {} |
||||
|
jackAudioOutLefts = {} |
||||
|
jackAudioOutRights = {} |
||||
|
outputMergerRouters = {} |
||||
|
routerToGlobalSummingStereoMixers = {} |
||||
|
lmixUuid = cbox.JackIO.create_audio_output('left_mix', "#1") #add "#1" as second parameter for auto-connection to system out 1 |
||||
|
rmixUuid = cbox.JackIO.create_audio_output('right_mix', "#2") #add "#2" as second parameter for auto-connection to system out 2 |
||||
|
cboxMidiPortUids = {} |
||||
|
sfzSamplerLayers = {} |
||||
|
instrumentLayers = {}\ |
||||
|
|
||||
|
|
||||
|
print (f"Creating {NUMBER_OF_INSTRUMENTS} instruments") |
||||
|
starttime = datetime.now() |
||||
|
|
||||
|
for i in range(NUMBER_OF_INSTRUMENTS): |
||||
|
scenes[i] = cbox.Document.get_engine().new_scene() |
||||
|
scenes[i].clear() |
||||
|
|
||||
|
#instrumentLayer = scenes[i].status().layers[0].get_instrument() |
||||
|
sfzSamplerLayers[i] = scenes[i].add_new_instrument_layer(str(i), "sampler") #"sampler" is the cbox sfz engine |
||||
|
scenes[i].status().layers[0].get_instrument().engine.load_patch_from_string(0, "", "", "") #fill with null instruments |
||||
|
|
||||
|
jackAudioOutLefts[i] = cbox.JackIO.create_audio_output(str(i) +"_L") |
||||
|
jackAudioOutRights[i] = cbox.JackIO.create_audio_output(str(i) +"_R") |
||||
|
|
||||
|
outputMergerRouters[i] = cbox.JackIO.create_audio_output_router(jackAudioOutLefts[i], jackAudioOutRights[i]) |
||||
|
outputMergerRouters[i].set_gain(-3.0) |
||||
|
#instrumentLayer = sfzSamplerLayers[i].get_instrument() |
||||
|
instrumentLayers[i] = scenes[i].status().layers[0].get_instrument() |
||||
|
instrumentLayers[i].get_output_slot(0).rec_wet.attach(outputMergerRouters[i]) #output_slot is 0 based and means a pair. Most sfz instrument have only one stereo pair. #TODO: And what if not? |
||||
|
|
||||
|
routerToGlobalSummingStereoMixers[i] = cbox.JackIO.create_audio_output_router(lmixUuid, rmixUuid) |
||||
|
routerToGlobalSummingStereoMixers[i].set_gain(-3.0) |
||||
|
instrument = sfzSamplerLayers[i].get_instrument() |
||||
|
instrument.get_output_slot(0).rec_wet.attach(routerToGlobalSummingStereoMixers[i]) |
||||
|
|
||||
|
#Create Midi Input Port |
||||
|
cboxMidiPortUids[i] = cbox.JackIO.create_midi_input(str(i) + "midi_in") |
||||
|
cbox.JackIO.set_appsink_for_midi_input(cboxMidiPortUids[i], True) #This sounds like a program wide sink, but it is needed for every port. |
||||
|
cbox.JackIO.route_midi_input(cboxMidiPortUids[i], scenes[i].uuid) #Route midi input to the scene. Without this we have no sound, but the python processor would still work. |
||||
|
|
||||
|
#Actually load the instrument |
||||
|
programNumber = 0 |
||||
|
#program = instrumentLayers[i].engine.load_patch_from_tar(programNumber, "bug.tar", f'Saw{1}.sfz', f'Saw{i+1}') #tar_name, sfz_name, display_name |
||||
|
program = instrumentLayers[i].engine.load_patch_from_string(programNumber, ".", "", "<group> <region> sample=*saw") #fill with null instruments |
||||
|
print (f"[{i}]", program.status()) |
||||
|
instrumentLayers[i].engine.set_patch(1, programNumber) #1 is the channel, counting from 1. #TODO: we want this to be on all channels. |
||||
|
|
||||
|
endtime = datetime.now() - starttime |
||||
|
print (f"Creation took {endtime.seconds} seconds for {NUMBER_OF_INSTRUMENTS} instruments") |
||||
|
|
||||
|
print() |
||||
|
print("Press Ctrl+C for a controlled shutdown.") |
||||
|
print() |
||||
|
|
||||
|
while True: |
||||
|
cbox.call_on_idle(cmd_dumper) |
@ -0,0 +1,39 @@ |
|||||
|
#! /usr/bin/env python3 |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from calfbox import cbox |
||||
|
|
||||
|
def cmd_dumper(cmd, fb, args): |
||||
|
print ("%s(%s)" % (cmd, ",".join(list(map(repr,args))))) |
||||
|
|
||||
|
cbox.init_engine() |
||||
|
cbox.start_audio(cmd_dumper) |
||||
|
|
||||
|
outportname = "CboxSendPattern" |
||||
|
cboxMidiOutUuid = cbox.JackIO.create_midi_output(outportname) #Add a named midi out port |
||||
|
cbox.JackIO.rename_midi_output(cboxMidiOutUuid, outportname) #For good measure. |
||||
|
outputScene = cbox.Document.get_engine().new_scene() #Create a new scene that will play the pattern. The pattern is not saved in the scene, it is not a track or so. |
||||
|
outputScene.clear() #For good measure. |
||||
|
outputScene.add_new_midi_layer(cboxMidiOutUuid) #Connect the scene to our midi output port. Without this there will be no midi out. |
||||
|
|
||||
|
|
||||
|
# Send 8 pitches 0x90 with velocity 0 |
||||
|
# Create a binary blob that contains the MIDI events |
||||
|
pblob = bytes() |
||||
|
for pitch in range(0,8): |
||||
|
# note on |
||||
|
pblob += cbox.Pattern.serialize_event(1, 0x90, pitch, 0) #tick in pattern, midi, pitch, velocity |
||||
|
# Create a new pattern object using events from the blob |
||||
|
allNoteOnZeroPattern = cbox.Document.get_song().pattern_from_blob(pblob, 0) #0 ticks. |
||||
|
|
||||
|
|
||||
|
print ("\nThis example sends midi events from a pattern without any tracks. Rolling transport or not doesn't matter.") |
||||
|
print("Ready!") |
||||
|
counter = 0 #To add delay |
||||
|
while True: |
||||
|
cbox.call_on_idle(cmd_dumper) |
||||
|
if counter > 10**5 * 4 : |
||||
|
print ("Send pattern") |
||||
|
outputScene.play_pattern(allNoteOnZeroPattern, 150.0) #150 tempo |
||||
|
counter = 0 |
||||
|
counter += 1 |
Loading…
Reference in new issue