FishBowl is central to the Sardine system. As its name might suggest, it is what holds everything together. Properly speaking, it is the environment, the water, what makes the fishes swim :) The system is composed of some hard dependencies and many soft dependencies. Hard dependencies are important components like the clock or the parser. They are needed all the time. As such, you can’t really remove them or everything would fall apart. Soft dependencies are the various senders or I/O components that you use to perform your music. Some of them are installed based on the content of your configuration, some can be created on the fly later on.
Core components cannot be removed from the
FishBowl. However, they can be swapped! It means that you can all of the sudden rip off the current clock and switch to a new one. The system might hiccup a bit but it will recover! To do so, note that you can use two important methods:
bowl.swap_clock(clock: "BaseClock"): swaps a clock.
LinkClock()are the two clocks currently implemented. The latter is used for synchronisation with every device capable of using the Ableton Link protocol.
bowl.swap_parser(parser: "BaseParser"): switch from a parser to another parser. There is no reason to do that because there is only one parser for the moment but it might be useful in the future.
This is where the fun begins. Pretty much everything in the Sardine system is a modular component that can be added or removed. Take a look at the
run.py file if you want to see how the system is first initialized. By default, Sardine is proposing a small collection of handlers / senders that will allow you to send or receive MIDI, OSC or SuperDirt messages. Some other handlers are used for various internal functions that you might not care about. Take a look at the following code detailing how to add modular components:
# Adding a MIDI Out handler: sending MIDI notes # control changes, program changes, etc... midi = MidiHandler(port_name=str(config.midi)) # new instance of the Handler bowl.add_handler(midi) # adding the handler to the FishBowl # OSC Loop: internal component used for handling OSC messages osc_loop = OSCLoop() # new instance of the Handler bowl.add_handler(osc_loop) # adding the handler to the FishBowl # OSC Handler: dummy OSC handler dummy_osc = OSCHandler( ip="127.0.0.1", port=12345, name="My OSC sender", ahead_amount=0.0, loop=osc_loop, ) # Aliasing some methods from the handlers for later :) M = midi.send CC = midi.send_control_changes PC = midi.send_program_changes O = dummy_osc.send
Please take note of the
bowl.add_handler method. If you don't add your component to the
FishBowl, your component will inevitably crash!
This is a fairly common mistake, especially if you are working in a hurry.
You might wonder what the
FishBowl is actually doing behind the scene. Factually, it allows component to talk with each other by sharing a reference to the bowl. It means that any component can send a message to any other component. It also means that this same component can promptly react to any event dispatched through the
FishBowl. Internal messages are sent using the
bowl.dispatch(message_type: str, *args) method. This is how messages such as
bowl.('play') are able to stop and resume everything when needed. They are messages dispatched to the
FishBowl making everyone aware that a major event occured.
- Introduction to the concept.
- Hard dependencies
- Soft dependencies
- Messaging system.