====== Python : Traitement du signal audio avec Pyo ====== ===== Présentation ===== Lien : * http://www.augmented-instruments.net/_media/pyo_as_python_dsp_toolbox.pdf * Site Web : http://ajaxsoundstudio.com/software/pyo/ * Documentation : http://ajaxsoundstudio.com/pyodoc/ Pyo est un module Python dédié au **traitement de signal audio** en temps réel. ===== Sommaire ===== #!/usr/bin/python3 #-*- coding: utf-8 -*- # Arnaud TECHER # SCEI 2019 N°43701 # Projet casque antibruit import wx,pyo,math, time from thread import * # classe pour le serveur audio class Serveur: def __init__(self): self.serveur=pyo.Server() #self.serveur.setOutputDevice(2) #self.serveur.setInputDevice(2) self.serveur.boot() #self.input = pyo.Input() self.serveur.amp = 0.4 #parametre pour la generation du son self.freqG = pyo.Sine(freq=300, phase=0, mul=0.1, add=0).out(0) self.freqD = pyo.Sine(freq=300, phase=0, mul=0.1, add=0).out(1) # parametre pour l'enregistrement self.enr = pyo.Input(chnl=0, mul=4.0) self.file = "enregistrement_tipe.wav" self.serveur.recordOptions(filename=self.file, fileformat=0, sampletype=1) # creation de la classe de l'application class Fenetre(wx.Frame): # le constructeur de la classe fenetre herite de wx.Frame, # il faut appeler le constructeur de wx.Frame : wx.Frame.__init__(). def __init__(self, parent, id, title, pos, size) : wx.Frame.__init__(self, parent, id, title,pos, size) self.parent = parent # var pour gerre slkider phase D self.iSliderD = 0 self.serveur() self.initialise() def serveur(self): self.audio = Serveur() def initialise(self): # creation d'un menu self.menu() # creation de l'interface # creation du panel et des box self.panel = wx.Panel(self) mainSizer = wx.BoxSizer(wx.VERTICAL) boxFrequence = wx.BoxSizer(wx.VERTICAL) boxAmplitude = wx.BoxSizer(wx.HORIZONTAL) gBoxAmplitude = wx.BoxSizer(wx.VERTICAL) dBoxAmplitude = wx.BoxSizer(wx.VERTICAL) boxPhase = wx.BoxSizer(wx.HORIZONTAL) gBoxPhase = wx.BoxSizer(wx.VERTICAL) dBoxPhase = wx.BoxSizer(wx.VERTICAL) boxCommande = wx.BoxSizer(wx.HORIZONTAL) # self.panel est le parent du widget, # wx.ID_ANY pour laisser wxPython choisir un identifiant # wx.EXPAND pour agrandir la cellule si la fenetre est agrandie # ajout dans les boxSizer # Bind pour associer la méthode a exécuter a l’evenement du widget # Gestion de la frequence # label self.labelFreq = wx.StaticText(self.panel,wx.ID_ANY, label=u'Frequence', style=wx.ALIGN_CENTRE_HORIZONTAL) boxFrequence.Add(self.labelFreq,0, wx.ALL | wx.EXPAND, 5) # slider self.freq=wx.Slider(self.panel,wx.ID_ANY,value=300,minValue=50,maxValue=1000, pos=(0,0),size=(200,-1),style=wx.SL_LABELS) boxFrequence.Add(self.freq,1, wx.ALL | wx.EXPAND, 5) mainSizer.Add(boxFrequence,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_SLIDER, self.changeFreq) # Gestion de l'amplitude # label gauche self.gLabelAmplitude = wx.StaticText(self.panel,wx.ID_ANY, label=u'Amplitude gauche',style=wx.ALIGN_RIGHT) gBoxAmplitude.Add(self.gLabelAmplitude,0, wx.ALL | wx.EXPAND, 5) # slider gauche self.gAmplitude=wx.Slider(self.panel,wx.ID_ANY,value=1,minValue=0,maxValue=10, pos=(0,0),size=(200,-1),style=wx.SL_LABELS) gBoxAmplitude.Add(self.gAmplitude,1, wx.ALL | wx.EXPAND, 5) boxAmplitude.Add(gBoxAmplitude,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_SLIDER, self.gChangeAmplitude, self.gAmplitude) # label droite self.dLabelAmplitude = wx.StaticText(self.panel,wx.ID_ANY, label=u'Amplitude droite',style=wx.ALIGN_RIGHT) dBoxAmplitude.Add(self.dLabelAmplitude,0, wx.ALL | wx.EXPAND, 5) # slider droite self.dAmplitude=wx.Slider(self.panel,wx.ID_ANY,value=1,minValue=0,maxValue=10, pos=(0,0),size=(200,-1),style=wx.SL_LABELS) dBoxAmplitude.Add(self.dAmplitude,1, wx.ALL | wx.EXPAND, 5) boxAmplitude.Add(dBoxAmplitude,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_SLIDER, self.dChangeAmplitude, self.dAmplitude) #ajout dans box main mainSizer.Add(boxAmplitude,1, wx.ALL | wx.EXPAND, 5) # Gestion de la phase gauche # label gauche self.gLabelPhase = wx.StaticText(self.panel,wx.ID_ANY, label=u'Phase gauche') gBoxPhase.Add(self.gLabelPhase,0, wx.ALL | wx.EXPAND, 5) # slider phase G self.gPhase=wx.Slider(self.panel,wx.ID_ANY,value=0,minValue=0,maxValue=100, pos=(0,0),size=(200,-1),style=wx.SL_LABELS) gBoxPhase.Add(self.gPhase,1, wx.ALL | wx.EXPAND, 5) boxPhase.Add(gBoxPhase,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_SLIDER, self.gChangePhase, self.gPhase) # label droite self.dLabelPhase = wx.StaticText(self.panel,wx.ID_ANY,label=u'Phase droite') dBoxPhase.Add(self.dLabelPhase,0, wx.ALL | wx.EXPAND, 5) # Gestion de la phase droite self.dPhase=wx.Slider(self.panel,wx.ID_ANY,value=0,minValue=0,maxValue=100, pos=(0,0),size=(200,-1),style=wx.SL_LABELS) dBoxPhase.Add(self.dPhase,0, wx.ALL | wx.EXPAND, 5) boxPhase.Add(dBoxPhase,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_SLIDER, self.dChangePhase, self.dPhase) #ajout dans box main mainSizer.Add(boxPhase,1, wx.ALL | wx.EXPAND, 5) # Gestion graphique du Scope dans une fenetre separee self.gscope = pyo.Scope([self.audio.freqG, self.audio.freqD]) # Gestion des boutons de commande #bouton star/stop self.boutonStart = wx.Button(self.panel,wx.ID_ANY,label="Start") boxCommande.Add(self.boutonStart,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_BUTTON, self.start, self.boutonStart) #bouton enregistrement self.boutonEnr = wx.Button(self.panel,wx.ID_ANY,label="Enr") boxCommande.Add(self.boutonEnr,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_BUTTON, self.enregistrement, self.boutonEnr) #bouton auto self.boutonAuto = wx.Button(self.panel,wx.ID_ANY,label="Auto") boxCommande.Add(self.boutonAuto,1, wx.ALL | wx.EXPAND, 5) self.Bind(wx.EVT_BUTTON, self.auto, self.boutonAuto) # ajout box main mainSizer.Add(boxCommande,1, wx.ALL | wx.EXPAND, 5) self.panel.SetSizerAndFit(mainSizer) def changeFreq(self,event): # modifier les frequences gauche et droite self.audio.freqG.setFreq(event.GetInt()) self.audio.freqD.freq=event.GetInt() def gChangeAmplitude(self,event): # modifier l'attribut mul self.audio.freqG.mul=event.GetInt()/10 def dChangeAmplitude(self,event): # modifier l'attribut mul self.audio.freqD.mul=event.GetInt()/10 def gChangePhase(self,event): # modifier l'attribut phase self.audio.freqG.setPhase(event.GetInt()/100) def dChangePhase(self,event): # modifier l'attribut phase self.audio.freqD.setPhase(event.GetInt()/100) def start(self,event): # demarer / arreter le serveur audio if self.boutonStart.GetLabel() == "Start": self.audio.serveur.start() self.boutonStart.SetLabel("Stop") else: self.audio.serveur.stop() self.boutonStart.SetLabel("Start") def enregistrement(self,event): # demarrer / arreter l'enregistrement if self.boutonEnr.GetLabel() == "Enr": self.audio.serveur.recstart() self.boutonEnr.SetLabel("Fin enr") else: self.audio.serveur.recstop() self.boutonEnr.SetLabel("Enr") def auto(self,event): # demarrer / arreter la variation automatique de la phase droite if self.boutonAuto.GetLabel() == "Auto": self.boutonAuto.SetLabel("Auto actif") duree = 0.01 self.t = Intervallometre(duree,self.augmenter) self.t.setDaemon(True) self.t.start() else: self.boutonAuto.SetLabel("Auto") self.iSliderD=0 self.audio.freqD.setPhase(0) self.t.stop() def augmenter(self): # augmenter la phase if self.iSliderD < 100: self.iSliderD += 1 self.audio.freqD.setPhase(self.iSliderD/100) self.dPhase.SetValue(self.iSliderD) def menu(self): # generation du menu filemenu= wx.Menu() # wx.ID_ABOUT et wx.ID_EXIT sont des IDs standards pour les wxWidgets. menuApropos = filemenu.Append(wx.ID_ABOUT, "&A propos","Information sur ce programme") filemenu.AppendSeparator() menuQuitter = filemenu.Append(wx.ID_EXIT,"&Quitter"," Quitter le programme") # Creation du menu. menuBar = wx.MenuBar() menuBar.Append(filemenu,"&Fichier") # Ajout de "filemenu" a la barre de Menu self.SetMenuBar(menuBar) # Ajout de la bare de menu au contenu de la fenetre # lier les evenements aux methodes self.Bind(wx.EVT_MENU, self.OnApropos, menuApropos) self.Bind(wx.EVT_MENU, self.OnQuitter, menuQuitter) def OnApropos(self,event): # Afficher une boite de dialogue avec un bouton OK. wx.OK est un ID standard des wxWidgets. dlg = wx.MessageDialog( self, "Arnaud TECHER - TIPE 2019", "Gestion des haut-parleurs", wx.OK) dlg.ShowModal() # afficher la bopite de dialogue par dessus la fenetre dlg.Destroy() # detruire la bopite de dialogue quand on clique sur OK ou que l'on, la ferme. def OnQuitter(self,event): # arreter le moteur audio self.serveur.serveur.stop() self.Close(True) # fermer la frenetre. if __name__ == "__main__": app = wx.App() fenetre_1 = Fenetre(None,wx.ID_ANY,'Pannel son1', (50,50), (1000,600)) # faire apparaitre la fenetre fenetre_1.Show() # boucle infinie qui attend les evenements utilisateur app.MainLoop() #!/usr/bin/python3 #-*- coding: utf-8 -*- # Arnaud TECHER # SCEI 2019 N°43701 # Gestion d'un thread import threading class Intervallometre(threading.Thread): def __init__(self, duree, fonction, args=[], kwargs={}): threading.Thread.__init__(self) self.duree = duree self.fonction = fonction self.args = args self.kwargs = kwargs self.encore = True # pour permettre l'arret a la demande def run(self): while self.encore: self.timer = threading.Timer(self.duree, self.fonction, self.args, self.kwargs) self.timer.setDaemon(True) self.timer.start() self.timer.join() def stop(self): self.encore = False # pour empecher un nouveau lancement de Timer et terminer le thread if self.timer.isAlive(): self.timer.cancel() # pour terminer une eventuelle attente en cours de Timer