bastibe / python-soundfile

SoundFile is an audio library based on libsndfile, CFFI, and NumPy

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Implementing a stop() method

mcavalca95 opened this issue · comments

I'm trying to implement an audio player using soundcard. The audio player must have a play() method that starts the audio playback, and a stop() method that stops the audio playback whenever it is called. I can't find a working implementation for the stop() method. The idea behind it is to use some sort of concurrency mechanism that can be stopped when the stop() method has been called.

The first implementation I tried was multiprocessing, because every process can be terminated with the method .terminate(). This solution doesn't work good for big projects, because the new process creation requires a notable amount of time and resources. So I tried with threading and the threading.Event object to stop the audio playback. In the code below you can see an example code:

import soundcard as sc
import soundfile as sf

default_speaker = sc.default_speaker()
file = sf.SoundFile(file="base trap.mp3")
frames = 1024 * 2 ** 10
stop = False

with default_speaker.player(
        samplerate=file.samplerate,
        channels=file.channels
) as sp:
    for _ in range(file.frames // frames):
        data = file.read(frames=frames)
        sp.play(data)

        if stop:
            break

The script instantiates a _Speaker through the speaker.player() method, then it starts a loop where the speaker.play() method is called with a part of the data. Each cycle of the for loop the data is updated. Between each speaker.play() call the method checks the value of the bool stop and, if True, it stops the loop. This value is set to True when the stop() method is called (in this example this mechanism is not implemented, there is only the value check). This way I can check if the stop() method has been called and stop the cycle. The issue that I notice is that there are always some glitches in the playback. What am I doing wrong? Do you have any suggestion on how to implement the stop() method?

Soundcard is a real-time framework. It expects you to provide/read data in small chunks. If you want to stop playing, stop providing data.

If instead you want to play entire files, and possibly stop them before they're done, you could use much simpler libraries, such as SDL or PyGame (I have not researched libraries, there are probably much easier ones than these two).

You can of course use Soundcard for this purpose, too. A thread with a stop-variable is a reasonable way of doing this. If you get playback glitches, you are not providing data fast enough. Being a real-time framework, the sound card probably expects data every 1000-or-so samples. This is highly dependent on the operating system. You can provide the player with a custom block size, but whether the OS will actually honor your request can not be guaranteed. Such are the vagaries of real-time audio processing.