python-2.7tkinterfluidsynth

Stop the reading of notes created with fluidsynth with a button in tkinter


I'm on python2-7 I want to get a button in tkinter which stop the reading of the notes created with fluidsynth.

I found that the common solution is to use time.after like here: How do you create a Tkinter GUI stop button to break an infinite loop?

But in my case I can't use it because I need an amount of time between noteon and noteoff to give durations for my notes. Moreover I want to play the notes ONLY if I click on start (and no at the beginning like the solution in the link).

So I created this code but it doesn't work because var_start is always initialized as int:

from tkinter import*
import fluidsynth
import time

fs=fluidsynth.Synth()
fs.start(driver='alsa', midi_driver='alsa_seq')
org_charge = fs.sfload("organ.sf2")
fs.program_select(0,org_charge, 0, 0)
time.sleep(1)

var_start=int

def start():
    global var_start
    var_start=1

def stop():
    global var_start
    var_start=0

root=Tk()

if var_start==1:
    fs.noteon(0,67,127)
    time.sleep(1)
    fs.noteoff(0,67)
    fs.noteon(0,71,127)
    time.sleep(1)
    fs.noteoff(0,71)
    fs.noteon(0,74,127)
    time.sleep(1)
    fs.noteoff(0,74)

Button(root, text='start', command= start).pack(padx=10, pady=10)    
Button(root, text='stop', command= stop).pack(padx=10, pady=10)    

root.mainloop()

I don't have other idea to reshape my code... Can someone help me ?

Thanks


Solution

  • You initiated var_start to int in statement var_start=int and so the code block in if var_start==1: will never be executed. And your start() function is just changing the var_start to 1 and never start playing the notes, therefore nothing will happen.

    Never call time.sleep() in main thread as it will block the tkinter main loop. You can use .after(...) to simulate the play loop and below is a sample code block:

    playing = False
    
    def play_notes(notes, index, noteoff):
        global playing
        if noteoff:
            fs.noteoff(0, notes[index])
            index += 1      # next note
        if playing and index < len(notes):
            fs.noteon(0, notes[index], 127)
            # call noteoff one second later
            root.after(1000, play_notes, notes, index, True)
        else:
            # either stopped or no more note to play
            playing = False
            print('done playing')
    
    def start_playing():
        global playing
        if not playing:
            print('start playing')
            playing = True
            notes = [67, 71, 74, 88, 80, 91]
            play_notes(notes, 0, False)
        else:
            print('already playing')
    
    def stop_playing():
        global playing
        if playing:
            playing = False
            print('stop playing')
        else:
            print('nothing playing')
    
    Button(root, text='Start', command=start_playing).pack(padx=10, pady=10)
    Button(root, text='Stop', command=stop_playing).pack(padx=10, pady=10)
    

    It is just an example you can modify to suit your need.