Partage
  • Partager sur Facebook
  • Partager sur Twitter

PyAudio : Enregistre son puis associé Tkinter

Anonyme
    27 février 2018 à 14:06:49

    Bonjour à tous !

    Tout d'abord, je tiens à vous expliquer mon problème.

    J'aimerais réaliser un programme python qui permettrai qu'une personne enregistre des instructions à l'aide d'un micro branché à une Raspberry Pi.

    Cette personne aurait une IHM Tkinter permettant de choisir quand commencer l'enregistrement et quand l'arrêter. Puis par la suite, peut être quelque chose pour choisir où enregistrer le fichier audio.

    Je me suis donc renseigné et j'ai trouvé le module PyAudio qui a l'air de bien marché et j'ai ce code qui me permet d'enregistrer le micro pendant un temps donné : 

    import pyaudio
    import wave
     
    FORMAT = pyaudio.paInt16
    CHANNELS = 2
    RATE = 44100
    CHUNK = 1024
    RECORD_SECONDS = 5
    WAVE_OUTPUT_FILENAME = "test.wav"
     
    audio = pyaudio.PyAudio()
     
                                            # Debut enregistrement
    stream = audio.open(format=FORMAT, channels=CHANNELS,
                    rate=RATE, input=True,
                    frames_per_buffer=CHUNK)
    print "Enregistrement en cours..."
    
    frames = []
     
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)
        
    print "Fin d'enregistrement"
     
     
                                            # Fin enregistrement
    stream.stop_stream()
    stream.close()
    audio.terminate()
     
    waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    waveFile.setnchannels(CHANNELS)
    waveFile.setsampwidth(audio.get_sample_size(FORMAT))
    waveFile.setframerate(RATE)
    waveFile.writeframes(b''.join(frames))
    waveFile.close()

    Par la suite, j'ai réalisé une IHM simple avec comme instructions,"Commencer l'enregistrement" et "Arrêter l'enregistrement" : 

    from Tkinter import *
    
    fenetre = Tk()
    
    fenetre.geometry("250x200")
    fenetre.resizable(width=False, height=False)
    
    label = Label(fenetre, text="Effectuer un enregistrement")
    label.place(x=50, y=0)
    
    bou1=Button(fenetre, text="Commencer l'enregistrement")
    bou2=Button(fenetre,text="Arreter l'enregistrement")
    bou3=Button(fenetre, text="Quitter", command=fenetre.destroy)
    bou1.place(x=20,y=60)
    bou2.place(x=20,y=100)
    bou3.place(x=175,y=150)
    
    fenetre.mainloop()
    

    Le problème vient maintenant lorsque je dois associer cette IHM à mon programme, j'ai donc essayé en créant des méthodes mais je suis bloqué, j'aimerais qu'il n'y ai pas de temps imparti mais que la personne puisse arrêter l'enregistrement quand elle le veut.

    J'ai réalisé ceci pour l'instant : 

    from Tkinter import *
    import pyaudio
    import wave
    
    frames=[]
    FORMAT = pyaudio.paInt16
    CHANNELS = 2
    RATE = 44100
    CHUNK = 1024
    RECORD_SECONDS = 5
    WAVE_OUTPUT_FILENAME = "testfile.wav"
     
    audio = pyaudio.PyAudio() #On instancie PyAudio
     
    def StartRecording():                             # Debut enregistrement
        
        stream = audio.open(format=FORMAT, channels=CHANNELS,
                        rate=RATE, input=True,
                        frames_per_buffer=CHUNK)
        print "Enregistrement en cours..."
    
        frames = []
     
        for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
            data = stream.read(CHUNK)
            frames.append(data)
    
    def StopRecording(): 
    
        stream.stop_stream()
        stream.close()
        audio.terminate()
        print "Fin d'enregistrement"
                                                        # Fin enregistrement
    
    waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    waveFile.setnchannels(CHANNELS)
    waveFile.setsampwidth(audio.get_sample_size(FORMAT))
    waveFile.setframerate(RATE)
    waveFile.writeframes(b''.join(frames))
    waveFile.close()
    
    
    fenetre = Tk()
    
    fenetre.geometry("250x250")
    fenetre.resizable(width=False, height=False)
    
    label = Label(fenetre, text="Effectuer un enregistrement")
    label.place(x=50, y=0)
    
    bou1=Button(fenetre, text="Commencer l'enregistrement", command = StartRecording())
    bou2=Button(fenetre, text="Arreter l'enregistrement", command = StopRecording())
    bou3=Button(fenetre, text="Quitter", command=fenetre.destroy)
    bou1.place(x=20,y=60)
    bou2.place(x=20,y=100)
    bou3.place(x=175,y=150)
    
    fenetre.mainloop()    
    

    Une fois cela réaliser, je devrais faire en sorte d'envoyer le fichier audio vers un serveur python mais je pense que ça sera plus facile.

    Je tiens à préciser que je débute en Python donc désolé si vous voyez de grossières erreurs.

    Je vous remercie d'avance pour votre aide.

    • Partager sur Facebook
    • Partager sur Twitter
      27 février 2018 à 16:43:40

      Salut,

      Le problème est que tu utilises PyAudio comme dans cet exemple : https://people.csail.mit.edu/hubert/pyaudio/docs/#example-blocking-mode-audio-i-o. C'est à dire que le code est bloquant. Chaque appel à data = stream.read(CHUNK) va bloquer le reste du programme.

      Dans ton petit exemple c'est pas important. Mais dès que tu veux utiliser tkinter, c'est un problème puisque la boucle principale à besoin de traiter les événements de l'utilisateur, comme son clic sur le bouton arrêter.

      Il va donc falloir utiliser la méthode non bloquante décrite ici : https://people.csail.mit.edu/hubert/pyaudio/docs/#example-callback-mode-audio-i-o

      Il y a pas mal de choses à expliquer je le crains. La documentation pour le comportement du callback se trouve ici : https://people.csail.mit.edu/hubert/pyaudio/docs/#pyaudio.Stream.__init__

      Comme tu le vois, le callback doit retourner un tuple avec (out_data, flag). Dans ton cas, comme c'est un enregistrement, ce sera (None, flag). Le flag sera paContinue tant qu'on veut enregistrer. Pour arrêter l'enregistrement il suffit de passer comme flag paComplete. Mais comment savoir quand arrêter ?

      Comme tu l'as dit, ce sera quand l'utilisateur aura pousser sur le bouton d'arrêt. Donc le fait de cliquer sur le bouton d'arrêt devrait passer une variable (une sorte de flag) de la valeur True à False. La valeur de cette variable est vérifée par ta fonction callback à chaque fois que PyAudio l'appelle afin de déterminer si on continue l'enregistrement ou pas.

      • Partager sur Facebook
      • Partager sur Twitter
      Anonyme
        13 mars 2018 à 9:02:28

        Bonjour,

        Tout d'abord merci pour votre réponse.

        Je suis en train d'essayer de modifier le programme en fonction de ce que vous m'avez dit, je vois un peu vers où il faut que je me dirige maintenant car j'avoue que j'étais un peu perdu.

        J'aimerais juste savoir s'il était plus facile pour la suite de mon programme de créer une classe ou bien de rester comme ça avec plusieurs fonctions ?

        • Partager sur Facebook
        • Partager sur Twitter
        Anonyme
          14 mars 2018 à 9:38:54

          J'ai finalement trouvé un code qui, je crois, fais dont ce j'ai besoin, c'est à dire enregistrer, arrêter un enregistrement, avec un mode callback qui permet de choisir quand l'arrêter, j'y ai ajouter mon interface Tkinter mais j'aurais quelques questions à propos de ce code, je les ai mise en commentaire : 

          from Tkinter import *
          import pyaudio
          import wave
          
          class Recorder(object):    #A quoi sert cette classe ?
          
              def __init__(self, channels=2, rate=44100, frames_per_buffer=1024):
                  self.channels = channels
                  self.rate = rate
                  self.frames_per_buffer = frames_per_buffer
          
              def open(self, fname, mode='wb'):
                  return RecordingFile(fname, mode, self.channels, self.rate,
                                      self.frames_per_buffer)
          
          
          class Recording(object):
          
              def __init__(self, mode, filename, CHANNELS=2, RATE=44100, CHUNK=1024):
          
                  self.filename = filename
                  self.mode=mode
                  self.CHANNELS = CHANNELS                                        
                  self.RATE = RATE                                        
                  self.CHUNK = CHUNK                                        
                  self.audio = pyaudio.PyAudio()
                  self.wavefile = self.Prepare_File(self.filename)
                  self._stream = None
          
              def Record(self, duration):        #Cette fonction me sert à rien car j'ai besoin du mode non bloquant, c'est ça ?
          
                  self.stream = self.audio.open(format=pyaudio.paInt16, channels=self.CHANNELS,
                                                rate=self.RATE,input=True, frames_per_buffer=self.CHUNK)
          
          
                  for i in range(0, int(self.RATE / self.CHUNK * NBSEC)):
                      data = self._stream.read(self.CHUNK)                   
                      self.frames.append(data)
                  return None
          
              def StartRecording(self):
                  
                  self._stream = self.audio.open(format=pyaudio.paInt16, channels=self.CHANNELS, rate=self.RATE,
                                                 input=True, frames_per_buffer=self.CHUNK,
                                                 stream_callback=self.Get_Callback())
                  self._stream.start_stream()
                  print "Commencer l'enregistrement"
                  return self
              
              def StopRecording(self):                   
          
                  self._stream.stop_stream()
                  print "Arrêter l'enregistrement"
                  return self
              
          
              def Get_Callback(self):         #C'est le mode Callback dont j'ai besoin n'est ce pas ?
          
                  def callback(in_data, frame_count, time_info, status):
                      self.wavefile.writeframes(in_data)
                      return in_data, pyaudio.paContinue
                  
                  return callback
          
              def close(self):
          
                  self.stream.close()
                  self.audio.terminate()
                  self.wavefile.close()
          
              def Prepare_File(self, filename, mode='wb'):   #Cette foncton sert à enregistrer le son dans un fichier, c'est ça ?
                  
                  waveFile = wave.open(filename, mode)
                  waveFile.setnchannels(self.CHANNELS)
                  waveFile.setsampwidth(self.audio.get_sample_size(pyaudio.paInt16))
                  waveFile.setframerate(self.RATE)
                  return waveFile
          
          
          
          test = Recording('test2.wav','wb')                  
          
          window = Tk()
          
          window.geometry("250x250")
          window.resizable(width=False, height=False)
          
          label = Label(fenetre, text="Effectuer un enregistrement")
          label.place(x=50, y=0)
          
          bou1=Button(window, text="Commencer l'enregistrement", command = test.StartRecording())
          bou2=Button(window, text="Arrêter l'enregistrement", command = test.StopRecording())
          bou3=Button(window, text="Quitter", command=window.destroy)
          bou1.place(x=20,y=60)
          bou2.place(x=20,y=100)
          bou3.place(x=175,y=150)
          
          window.mainloop()    

          Le code originale provient d'ici : https://gist.github.com/sloria/5693955

          • Partager sur Facebook
          • Partager sur Twitter
            14 mars 2018 à 14:44:56

            Salut,

            La classe Recorder ne sert pas à grand chose dans ton cas.

            La méthode Record est en effet inutile pour toi. La méthode Prepare_File sert à ouvrir le fichier wav en écriture, et y placer les données d'en-tête comme le nombre de canaux, etc. Get_Callback est compliquée pour rien.

            Voici un autre exemple que j'ai fait. J'ai créé une classe RecorderFrame qui est une Frame à placer où tu veux dans ton programme. Elle ouvre un fichier et enregistre dedans le son enregistré jusqu'à ce qu'on presse le bouton d'arrêt.

            #!/usr/bin/env python3.6
            # -*- coding:  utf-8 -*-
            
            import tkinter as tk
            import tkinter.filedialog
            import wave
            import pyaudio
            
            
            class RecordingFile:
                def __init__(self, filename, mode='wb', CHANNELS=2, RATE=44100, CHUNK=1024):
            
                    self.filename = filename
                    self.mode = mode
                    self.CHANNELS = CHANNELS
                    self.RATE = RATE
                    self.CHUNK = CHUNK
                    self.audio = pyaudio.PyAudio()
                    self.wavefile = self.prepare_file(self.filename)
                    self._stream = None
                    self.recording = False
            
                def start_recording(self):
                    self._stream = self.audio.open(format=pyaudio.paInt16,
                                                   channels=self.CHANNELS, rate=self.RATE,
                                                   input=True, frames_per_buffer=self.CHUNK,
                                                   stream_callback=self.write_callback)
                    self._stream.start_stream()
                    self.recording = True
            
                def stop_recording(self):
                    self.recording = False
                    if self._stream:
                        self._stream.stop_stream()
                        self.close()
            
                def write_callback(self, in_data, frame_count, time_info, status):
                    self.wavefile.writeframes(in_data)
                    if self.recording:
                        flag = pyaudio.paContinue
                    else:
                        flag = pyaudio.paComplete
                    return None, flag
            
                def close(self):
                    if self._stream:
                        self._stream.close()
                        self._stream = None
                        self.audio.terminate()
                        self.wavefile.close()
            
                def prepare_file(self, filename, mode='wb'):
                    wavefile = wave.open(filename, mode)
                    wavefile.setnchannels(self.CHANNELS)
                    wavefile.setsampwidth(self.audio.get_sample_size(pyaudio.paInt16))
                    wavefile.setframerate(self.RATE)
                    return wavefile
            
            
            class RecorderFrame(tk.Frame):
                def __init__(self, *args, **kwargs):
                    super().__init__(*args, **kwargs)
            
                    self.grid_columnconfigure(0, weight=1)
                    self.grid_rowconfigure(0, weight=1)
            
                    label = tk.Label(self, text="Effectuer un enregistrement")
                    label.grid(padx=10, pady=(5, 15), sticky=tk.EW)
            
                    self.rf = None
                    self.action = tk.Button(self, text="Commencer l'enregistrement",
                                            command=self.start_recording)
                    self.action.grid(padx=10, pady=5, ipadx=5, ipady=5, sticky=tk.EW)
            
                def start_recording(self):
                    filename = tkinter.filedialog.asksaveasfilename()
                    self.rf = RecordingFile(filename)
                    self.rf.start_recording()
                    self.action.configure(text="Arrêter l'enregistrement",
                                          command=self.stop_recording)
            
                def stop_recording(self):
                    self.rf.stop_recording()
                    self.rf = None
                    self.action.configure(text="Commencer l'enregistrement",
                                          command=self.start_recording)
            
            
            if __name__ == "__main__":
                window = tk.Tk()
                window.grid_columnconfigure(0, weight=1)
                window.grid_rowconfigure(0, weight=1)
            
                window.geometry("250x250")
                window.resizable(width=False, height=False)
            
                main_content = RecorderFrame(window)
                main_content.grid(sticky=tk.NSEW)
            
                quit = tk.Button(window, text="Quitter", command=window.destroy)
                quit.grid(padx=10, pady=5, ipadx=5, ipady=5, sticky=tk.EW)
            
                window.mainloop()
            

            -
            Edité par Dan737 2 août 2018 à 16:41:31

            • Partager sur Facebook
            • Partager sur Twitter
            Anonyme
              20 mars 2018 à 11:35:14

              Salut ! 

              Merci beaucoup pour ton aide.

              J'aimerais juste savoir à quoi sert la fonction super() dans la classe RecordingFrame ? 

              Et que veulent dire le (**kwargs) ?

              Merci.

              -
              Edité par Anonyme 20 mars 2018 à 11:42:19

              • Partager sur Facebook
              • Partager sur Twitter
                31 octobre 2020 à 15:13:16

                Bonjour


                Tout d’abord merci pour cette discussion qui m’a déjà beaucoup appris.

                J’ai cependant une question concernant l’enregistrement du fichier.

                En effet avec la méthode de Dan737 nous devons choisir un emplacement de sauvegarde du fichier et il est donc impossible de le télécharger à même le projet pour pouvoir ensuite directement le traiter ( je suis sur un programme de traitement de signal) contrairement à la méthode utilisant un code bloquant, qui elle me permet d’automatiquement d’avoir le fichier exploitable à même le projet PyCharm.

                Dans mon cas, utilisant une IHM Tinker je ne vois pas comment je pourrais obtenir le même résultat...

                Je vous remercie d’avance pour toute l’aide qui pourra m’être apporté.

                • Partager sur Facebook
                • Partager sur Twitter

                PyAudio : Enregistre son puis associé Tkinter

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown