Using Scamp to send and receive midi data


I am just getting started with scamp and putting together a program that receives data from a keyboard, modifies it in some form and then sends it to a midi player.

My code and set up are shown below:
If I connect the virtual keyboard to QSynth then everything works fine. I create a midi channel in LoopMidi. I connect virtualkeys and QSynth to that channel and as I play notes I can hear them on QSynth.
If I try and route through Scamp then when I play a key on virtual keys I see that the note is received, but as I play it the note sounds indefinitely until it fades out over a few seconds and then no subsequent notes are received on the listener.
It seems like the played note has gotten stuck and is blocking any further events, both on the incoming and outgoing channels.

If I do not listen and play at the same time the code works fine. The problem only occurs when I try to play a note I have received.
Is this some form of threading issue or is there some way I can further debug it?

from scamp import *
from collections import namedtuple
import datetime


class Note:
    def __init__(self,pitch,volume,clock):
        self.__duration = 0.0
        self.__pitch = pitch
        self.__volume = volume
        self.__clock = clock
    def show(self):    
        return "Pitch {}, Volume = {},  clock = {}, Duration = {}".format(self.__pitch,self.__volume,self.__clock, self.__duration)   

    def duration(self):
        """The duration property."""
        return self.__duration

    def duration(self, value):
        self.__duration = value  

    def pitch(self):
        """The pitch property."""
        return self.__pitch

    def pitch(self, value):
        self.__pitch = value  

class Manipulator:
    def __init__(self):
        self.session = Session()
            self.piano = self.session.new_midi_part("piano", midi_output_device=4, num_channels=8) # Scamp to QSynth
            print("Failed to open MidiLoop Port for output")      

        self.onTime = None
        self.offTime = None

    def midi_callback(self,midi_message):
        code, pitch, volume = midi_message
        print("Keyboard {}".format(midi_message))
        if(volume > 0): # this is an on-event
            self.onTime =
            self.note = Note(pitch,volume,self.onTime.timestamp())

            self.offTime =
            delta = self.offTime - self.onTime
            self.note.duration = float(delta.total_seconds())

            # Eventually do something with the note here
                noteReference = self.piano.start_note(self.note.pitch, .8)

    def Run(self):

            self.session.register_midi_listener(3, self.midi_callback) #[Port 4]: Input from Virtual Keyboard
            print("Failed to open MidiLoop Port for input")                

m = Manipulator()

I placed an image of the setup here:


The midi callback function is designed to run all at once with no waiting in the middle. So any play_note call should be blocking=False and you should avoid wait.

If you want to do something that requires playing several notes in a row, or requires using wait, the thing to do is to take that functionality and put it in its own function, and then fork that function from the midi callback function. Does that make sense?

Hi Marc,
Happy new year from Ireland and thank you for your help. Using the fork solved the problem. I can now send the notes from a keyboard on one channel, have them processed by scamp and then send them to a virtual instrument like QSynth.
Many thanks for your help.

No problem, and cool to hear that SCAMP has made it to Ireland!

And yeah, I’ve used SCAMP this way before, for example when I wanted to use the sustain pedal on my piano to make notes duration actually last longer, instead of just send a pedal message.