Search code examples
pythonwxwidgetspyo

Binding a Pyo Oscillator output to a WX Event


I am building a simple signal generator in Python based on the Pyo and WX libraries.

I have ran through the simple tutorials for each and have successfully bound buttons in WX to WX functions. I am now trying to generate a simple sine wave(at 440 hz) for 1 second by pressing the button labeled "Oscillator 1"; however, when the main() function executes, the sine tone is played and while the button is displayed in the wx frame I am unable to retrigger the sine tone. Both of these symptoms are unwanted.

Why does the sine tone play immediately on program execution? Why does the firstOSC button seemingly not work?

import wx
from pyo import *
import time

pyoServer = Server().boot()  
pyoServer.start()

class MainWindow(wx.Frame):
    def __init__(self,parent,title):
        wx.Frame.__init__(self,parent,title=title, size = (640,640))
        self.CreateStatusBar() # A StatusBar in the bottom of the window        

        # Signal Generator controls
        oscillator = SoundOutput()
        firstOSC = wx.Button(self, wx.ID_YES,"Oscillator 1 " + str(oscillator.str_osc1State))
        self.Bind(wx.EVT_BUTTON, oscillator.OnOff1(440), firstOSC)

        #Menus
        filemenu = wx.Menu()
        menuExit = filemenu.Append(wx.ID_EXIT,"&Exit","Terminate the program")
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File")
        self.SetMenuBar(menuBar)    
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)

        self.Show(True)
    def OnExit(self,e):
        self.Close(True)   


class SoundOutput(object):
    def __init__(self):
        self.osc1State = False
        self.str_osc1State = "Off"
        self.a = Sine(440, 0, 0.1)     
    def OnOff1(self, frequency):
        self.a.freq = frequency
        self.a.out()
        time.sleep(1)
        self.osc1State = True

def Main():
    app = wx.App(False)
    frame = MainWindow(None,"Signal Generator")
    app.MainLoop()

Solution

  • I solved this by investigating how WX handles events. As it turns out, for some reason calling a method in a nested or separate instance of a class caused the tone to play at runtime instead of on the event. I fixed this by making a method for the MainWindow class that serves as the binded event handler for firstOSC. This method then calls the requisite methods for the actual oscillator class.

    Here is the new code:

        # Signal Generator controls
        self.fOscillator = SoundOutput()
        self.fOscillatorstatus = False
        self.firstOSC = wx.Button(self, wx.ID_ANY,"Oscillator 1 On")
        self.firstOSC.Bind(wx.EVT_BUTTON, self.OnFirstOSC)
    
        def OnFirstOSC(self,e):
        if not self.fOscillatorstatus:
            self.fOscillator.OnOff1(440) 
            self.fOscillatorstatus = True
            self.firstOSC.SetLabel("Oscillator 1 Off")
        elif self.fOscillatorstatus:
            self.fOscillator.OnOff1(0)
            self.firstOSC.SetLabel("Oscillator 1 On")
            self.fOscillatorstatus = False