[PYTHON] Play audio files with interrupts using PyAudio

Looking at the Python tags in order of new arrival, there was an article like this. Play audio file from Python with interrupt

I've used PyAudio, but I didn't use it that way, so I was reading while thinking, "I think I can do it with PyAudio, but can't I?"

I made it when I noticed it.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pyaudio
import wave
from time import sleep
import threading

CHUNK = 1024


class AudioPlayer:
    """ A Class For Playing Audio """

    def __init__(self):
        self.audio_file = ""

        #Flag to stop
        self.paused = threading.Event()

    def setAudioFile(self, audio_file):
        self.audio_file = audio_file

    def playAudio(self):
        if (self.audio_file == ""):
            return
        self.wf = wave.open(self.audio_file, "rb")
        p = pyaudio.PyAudio()

        self.stream = p.open(format=p.get_format_from_width(self.wf.getsampwidth()),
                             channels=self.wf.getnchannels(),
                             rate=self.wf.getframerate(),
                             output=True)

        data = self.wf.readframes(CHUNK)

        # play stream (3)
        while len(data) > 0:
            #If the stop flag is set
            if self.paused.is_set():
                #Stop playing
                self.stream.stop_stream()
                #Initialize the flag
                self.paused.clear()
                break
            self.stream.write(data)
            data = self.wf.readframes(CHUNK)

        # stop stream (4)
        self.stream.stop_stream()
        self.stream.close()
        # close PyAudio (5)
        p.terminate()


def play(player):
    #Play in a separate thread
    audio_thread = threading.Thread(target=player.playAudio)
    audio_thread.start()


if __name__ == "__main__":
    player1 = AudioPlayer()
    player1.setAudioFile("1.wav")
    player2 = AudioPlayer()
    player2.setAudioFile("2.wav")

    play(player1)
    #For example 0,Change to another sound source after 5 seconds
    sleep(0.5)
    #Stop 1
    player1.paused.set()
    #Play 2
    play(player2)

I was able to do it, but there was a ** bug **. .. .. If you stop the main part and play it again,

if __name__ == "__main__":
    player1 = AudioPlayer()
    player1.setAudioFile("1.wav")

    play(player1)
    #For example 0,Play the same again after 5 seconds
    sleep(0.5)
    #Stop 1
    player1.paused.set()
    #Play 1 again
    play(player1)

Python(39927,0x105607000) malloc: *** error for object 0x100886800: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
zsh: abort      python audio.py

I get an error, so as appropriate

if __name__ == "__main__":
    player1 = AudioPlayer()
    player1.setAudioFile("1.wav")

    play(player1)
    #For example 0,Play the same again after 5 seconds
    sleep(0.5)
    #Stop 1
    player1.paused.set()
    #Play 1 again
    
    #I get an error so I sleep
    sleep(0.6)
    play(player1)

Please put it to sleep. In my environment, I get this error if I don't rest for more than 0.6 seconds.

Recommended Posts

Play audio files with interrupts using PyAudio
Play audio files from Python with interrupts
Play with puns using the COTOHA API
Play with Prophet
Play with PyTorch
Play with 2016-Python
Play with CentOS 8
Play with Pyramid
Play with Fathom
Upload files with Django
Play with Othello (Reversi)
Discord Bot with recording function starting with Python: (4) Play music files
Play with YouTube Data API v3 using Google API Python Client