How to stop audio with playsound module? - python

How do I stop the audio playing through playaudio module in Python code?
I have played music but I can't stop that music. How can I stop it?
playsound.playsound("name_of_file")

You can use the multiprocessing module to play the sound as a background process, then terminate it anytime you want:
import multiprocessing
from playsound import playsound
p = multiprocessing.Process(target=playsound, args=("file.mp3",))
p.start()
input("press ENTER to stop playback")
p.terminate()

Playsound is a single function module that plays sounds, and nothing else. It would seem that means it does not stop playing sounds either. From their own documentation:
The playsound module contains only one thing - the function (also named) playsound.
Personally, I like to use pyaudio. The following code is adapted from the example here. The code plays audio and has the space bar set as a pause/play button.
import pyaudio
import wave
import time
from pynput import keyboard
paused = False # global to track if the audio is paused
def on_press(key):
global paused
print (key)
if key == keyboard.Key.space:
if stream.is_stopped(): # time to play audio
print ('play pressed')
stream.start_stream()
paused = False
return False
elif stream.is_active(): # time to pause audio
print ('pause pressed')
stream.stop_stream()
paused = True
return False
return False
# you audio here
wf = wave.open('audio\\songs\\And_Your_Bird_Can_Sing_mp3_2_wav.wav', 'rb')
# instantiate PyAudio
p = pyaudio.PyAudio()
# define callback
def callback(in_data, frame_count, time_info, status):
data = wf.readframes(frame_count)
return (data, pyaudio.paContinue)
# open stream using callback
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
stream_callback=callback)
# start the stream
stream.start_stream()
while stream.is_active() or paused==True:
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
time.sleep(0.1)
# stop stream
stream.stop_stream()
stream.close()
wf.close()
# close PyAudio
p.terminate()

On windows try:
import winsound
winsound.PlaySound(r'C:\sound.wav', winsound.SND_ASYNC)
Stop Playback:
winsound.PlaySound(None, winsound.SND_PURGE)

#pip install pygame
from pygame import mixer
import time
mixer.init() #Initialzing pyamge mixer
mixer.music.load('lovingly-618.mp3') #Loading Music File
mixer.music.play() #Playing Music with Pygame
time.sleep(5)
mixer.music.stop()

Here is a much easier way:
wmp = win32com.client.dynamic.Dispatch("WMPlayer.OCX")
wmp.settings.autoStart = True
wmp.settings.volume = 100
wmp.URL = file
while globals()["allowSound"]:
PumpWaitingMessages()
You can change globals()["allowSound"] from another thread and set it to false when the audio has ended (you can get the length of the audio with wmp.durationString)
Here is some more info about this: Windows Media Player COM Automation works from VBS but not from Python
Although this does not use the playsound module it is a good alternative.

After successful execution of the program, audio file will start playing. Now Click on terminal, once you see the cursor, type Ctrl+C, it will bring you out of the terminal and audio will also stop playing.
Programming Instructions used:
from playsound import playsound
playsound('//path//to//a//sound//file//you//want//to//play.mp3')

EASY......
on your terminal tab, look for kill(delete) option on the right hand side.
click the delete option and it will stop playing

Related

How to read RFID tags in one thread and playing audio files in parallel in another thread?

I'm trying to create some sort of Jukebox in Python. The Python code runs on a Raspberry Pi. The setup is as follows
a RFID reader (MFRC522) is connected to the Raspberry Pi correctly (tested) and will read RFID tags with information about a playlist (a list of songs to be played). It is controlled using the SimpleMFRC522 python library
the audio files all reside in a directory ./audio/downloads. The content of the RFID tag will contain the subdirectory for the playlist to be played. (e.g. myPlaylist for ./audio/downloads/myPlaylist). The sound is played using the PyGame Library
some buttons to control the audio are connected to play/pause and skip to the previous/next track. The Buttons are controlled using the Python GPIO Zero library
You can assume the hardware is correctly wired. I checked with some basic scripts (see bottom of this post).
My Python script is this:
# mironibox.py
import threading
from os import listdir
from os.path import isfile, join
import RPi.GPIO as GPIO
from gpiozero import Button
from mfrc522 import SimpleMFRC522
from pygame import mixer
playlist = []
current_track_number = 0
reader = SimpleMFRC522()
button_prev = Button(20)
button_next = Button(16)
button_play_pause = Button(21)
mixer.init()
def read_rfid():
global playlist, current_track_number
while True:
playlist_id, playlist_dir = reader.read()
print(f'read RFID id={playlist_id}: {playlist_dir}')
path = f'./audio/downloads/{playlist_dir.strip()}/'
playlist = sorted([join(path, f) for f in listdir(path) if isfile(join(path, f))])
play(0)
def play(track_number):
global playlist, current_track_number
print(playlist[track_number])
mixer.music.load(playlist[track_number])
mixer.music.play()
current_track_number = track_number
if __name__ == '__main__':
try:
button_prev.when_released = lambda: play(current_track_number - 1)
button_next.when_released = lambda: play(current_track_number + 1)
button_play_pause.when_released = lambda: mixer.music.pause()
rfid = threading.Thread(name='rfid', target=read_rfid, daemon=True)
rfid.start()
print('Waiting for RFID input')
rfid.join() # prevent main thread from exiting
finally:
print('cleaning up')
GPIO.cleanup()
As you can see, the RFID reader is a simple infinite loop that will initialize the playlist array with a list of strings, each one representing the absolute path to a file. I tested with a RFID tag containing the string dieter_wiesenmann_matthias.
This works in a terminal:
$ mplayer ./audio/downloads/dieter_wiesenmann_matthias/01.mp3
... (file is played)
However, when running the script and reading a RFID-tag containing dieter_wiesenmann_matthias, the playlist is created correctly and the mixer is loaded with the correct audio file. But I can hear no sound.
$ python mironibox.py
Waiting for RFID input
/usr/lib/python3/dist-packages/gpiozero/devices.py:288: PinFactoryFallback: Falling back from rpigpio: A different mode has already been set!
warnings.warn(
/usr/lib/python3/dist-packages/gpiozero/devices.py:288: PinFactoryFallback: Falling back from lgpio: No module named 'lgpio'
warnings.warn(
/usr/lib/python3/dist-packages/gpiozero/devices.py:288: PinFactoryFallback: Falling back from rpio: No module named 'RPIO'
warnings.warn(
/usr/lib/python3/dist-packages/gpiozero/devices.py:288: PinFactoryFallback: Falling back from pigpio: No module named 'pigpio'
warnings.warn(
/usr/lib/python3/dist-packages/gpiozero/devices.py:285: NativePinFactoryFallback: Falling back to the experimental pin factory NativeFactory because no other pin factory could be loaded. For best results, install RPi.GPIO or pigpio. See https://gpiozero.readthedocs.io/en/stable/api_pins.html for more information.
warnings.warn(NativePinFactoryFallback(native_fallback_message))
read RFID id=1004807035305: dieter_wiesenmann_matthias
playing: ./audio/downloads/dieter_wiesenmann_matthias/01.mp3
Strangely enough, trying to play the file from the terminal does not work anymore after running the Python script once. I have to reboot the Raspberry to make it work again. Not sure if this is related.
I suspect there's something wrong with the Multithreading, like the main thread being blocked by the RFID-Thread. I'm also not sure about the warnings on the console.
Test scripts I used to check the python code to read an RFID tag or play an audio file
This will print out the content of a RFID tag
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
reader = SimpleMFRC522()
try:
id, text = reader.read()
print(id)
print(text)
finally:
GPIO.cleanup()
This will play a 5-second test file:
import time
from pygame import mixer
mixer.init()
mixer.music.load('./audio/downloads/dieter_wiesenmann_matthias/01.mp3')
mixer.music.play()
time.sleep(10) # prevent script from exiting before player has played a bit of the file

How can I play another music list with Pygame? [duplicate]

I am looking to iterate through a list of songs such as Songs = ["Song1.mp3", "Song2.mp3", "Song3.mp3"] and I want to play each song one after each other.
I have tried various methods, the most suggested seemed to use pygame, however, I have not been able to debug the tremendous amount of errors that come with using it. My main source code and attempt at this is as shown below:
from tkinter import *
import pygame
from random import choice
import os
pygame.mixer.init()
Songs = os.listdir("Music\\")
def Play():
Song = choice(Songs)
pygame.mixer.music.load("Music\\" + Song)
pygame.mixer.music.play()
while True:
play()
Upon running this I receive error pygame.error: ModPlug_Load failed.
I am running this concurrently inside of a slideshow program I have, I want this code to run as background music and I plan on checking for the end of the song in a Function I already have set.
Use pygame.mixer.music.get_busy() to detect if a music stream is actively playing. Play the next song from the list when no stream is active. e.g:
import pygame
play_list = ["song1.mp3", "song2.mp3", "song3.mp3"]
current_list = []
pygame.init()
clock = pygame.time.Clock()
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if not pygame.mixer.music.get_busy():
if not current_list:
current_list = play_list[:]
current_song = current_list.pop(0)
pygame.mixer.music.load(current_song)
pygame.mixer.music.play()
pygame.quit()
exit()
You can register a custom event which will be triggered when the music is done playing using pygame.mixer.music.set_endevent().
Also you need to run the while loop in a child thread if you want to run concurrently with main application.
Below is an example:
import os
import random
import threading
import tkinter as tk
import pygame
pygame.init()
# create a custom event
MUSIC_DONE = pygame.event.custom_type()
# register the event
pygame.mixer.music.set_endevent(MUSIC_DONE)
folder = "Music\\"
Songs = os.listdir(folder)
def next_song():
try:
song = random.choice(Songs)
pygame.mixer.music.load(os.path.join(folder, song))
pygame.mixer.music.play()
# update song name
song_var.set(song)
except Exception as e:
print(e)
def pygame_loop():
next_song()
while pygame.get_init():
for event in pygame.event.get():
if event.type == MUSIC_DONE:
# current song done playing, play next song
next_song()
root = tk.Tk()
# label to show the current song being played
song_var = tk.StringVar()
tk.Label(root, textvariable=song_var).pack()
# start the pygame loop in a child thread
threading.Thread(target=pygame_loop).start()
root.mainloop()
# quit pygame
pygame.quit()
If it's a desktop app, you have multiple options to play a file, usually, it depends:
Also to avoid Tkinter being stuck, you need to use threads or multiprocess. I recommend you soundfile player, you will find examples here https://realpython.com/playing-and-recording-sound-python/

How can I Iterate through a list of songs and play them one after eachother

I am looking to iterate through a list of songs such as Songs = ["Song1.mp3", "Song2.mp3", "Song3.mp3"] and I want to play each song one after each other.
I have tried various methods, the most suggested seemed to use pygame, however, I have not been able to debug the tremendous amount of errors that come with using it. My main source code and attempt at this is as shown below:
from tkinter import *
import pygame
from random import choice
import os
pygame.mixer.init()
Songs = os.listdir("Music\\")
def Play():
Song = choice(Songs)
pygame.mixer.music.load("Music\\" + Song)
pygame.mixer.music.play()
while True:
play()
Upon running this I receive error pygame.error: ModPlug_Load failed.
I am running this concurrently inside of a slideshow program I have, I want this code to run as background music and I plan on checking for the end of the song in a Function I already have set.
Use pygame.mixer.music.get_busy() to detect if a music stream is actively playing. Play the next song from the list when no stream is active. e.g:
import pygame
play_list = ["song1.mp3", "song2.mp3", "song3.mp3"]
current_list = []
pygame.init()
clock = pygame.time.Clock()
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if not pygame.mixer.music.get_busy():
if not current_list:
current_list = play_list[:]
current_song = current_list.pop(0)
pygame.mixer.music.load(current_song)
pygame.mixer.music.play()
pygame.quit()
exit()
You can register a custom event which will be triggered when the music is done playing using pygame.mixer.music.set_endevent().
Also you need to run the while loop in a child thread if you want to run concurrently with main application.
Below is an example:
import os
import random
import threading
import tkinter as tk
import pygame
pygame.init()
# create a custom event
MUSIC_DONE = pygame.event.custom_type()
# register the event
pygame.mixer.music.set_endevent(MUSIC_DONE)
folder = "Music\\"
Songs = os.listdir(folder)
def next_song():
try:
song = random.choice(Songs)
pygame.mixer.music.load(os.path.join(folder, song))
pygame.mixer.music.play()
# update song name
song_var.set(song)
except Exception as e:
print(e)
def pygame_loop():
next_song()
while pygame.get_init():
for event in pygame.event.get():
if event.type == MUSIC_DONE:
# current song done playing, play next song
next_song()
root = tk.Tk()
# label to show the current song being played
song_var = tk.StringVar()
tk.Label(root, textvariable=song_var).pack()
# start the pygame loop in a child thread
threading.Thread(target=pygame_loop).start()
root.mainloop()
# quit pygame
pygame.quit()
If it's a desktop app, you have multiple options to play a file, usually, it depends:
Also to avoid Tkinter being stuck, you need to use threads or multiprocess. I recommend you soundfile player, you will find examples here https://realpython.com/playing-and-recording-sound-python/

Raspberry pi I2S MEMS Microphone Right CHN Mono Using pyaudio

I am using Adafruit I2S MEMS Microphone Breakout for recording. ref. https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout?view=all
When i wire the Mic to RPI in Mono configuration as per below image I am able record audio using arecord command and below python code
arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono -v recording.wav
Python Code snippet:
channels=1, rate=48000, frames_per_buffer=2400
def start_recording(self):
try:
self.logger.info("start_recording()> enter")
# Use a stream with a callback in non-blocking mode
self._stream = self._pa.open(format=pyaudio.paInt32,
channels=self.channels,
rate=self.rate,
input=True,
frames_per_buffer=self.frames_per_buffer,
stream_callback=self.get_callback())
self._stream.start_stream()
self.logger.info("start_recording()> exit")
return self
except Exception, e:
self.logger.error("start_recording()>", exc_info = True)
But If I connect channel selection pin to logic high vltage i am able to record audio using arecord command but uanble to record using python code.
Any changes required in python code to record right channel mono audio?
I did something similar but using python-sounddevice. Here's my repo
EDIT:
here is the specific audio recording class for clarification
import threading
import queue
import numpy
import sounddevice as sd
import soundfile as sf
class AudioRecorder():
def __init__(self):
self.open = True
self.file_name = 'name_of_file.wav'
self.channels = 1
self.q = queue.Queue()
# Get samplerate
device_info = sd.query_devices(2, 'input')
self.samplerate = int(device_info['default_samplerate'])
def callback(self, indata, frames, time, status):
# This is called (from a separate thread) for each audio block.
if status:
print(status, file=sys.stderr)
self.q.put(indata.copy())
def record(self):
with sf.SoundFile(self.file_name, mode='x', samplerate=self.samplerate, channels=self.channels) as file:
with sd.InputStream(samplerate=self.samplerate, channels=self.channels, callback=self.callback):
while(self.open == True):
file.write(self.q.get())
EDIT 2: The code is a Python Class that creates an audio file using an I2S microphone similar to the image shown in the question. While the value self.open is true, sounddevice will write the audio data into a queue (def callback) and then write the data into a file. All you have to do is toggle self.open to start and stop recording.

Error while re-opening sound file in python

I was in the process of making a program that simply repeats any text you enter, and seemed to be working when I first tested it. The problem is that the second time I attempt to type anything, it crashes and says that permission was denied to the sound file I was recording it to. I believe it is because the file was already opened, but none the less I do not know how to fix it. I am using the gTTS and Pygame modules.
from gtts import gTTS
from tempfile import TemporaryFile
from pygame import mixer
#Plays Sound
def play():
mixer.init()
mixer.music.load("Speech.mp3")
mixer.music.play()
#Voice
def voice(x):
text = gTTS(text= x, lang= 'en')
with open("Speech.mp3", 'wb') as f:
text.write_to_fp(f)
f.close()
play()
#Prompts user to enter text for speech
while True:
voice_input = input("What should Wellington Say: ")
voice(voice_input)
Figured it out. I added this function:
def delete():
sleep(2)
mixer.music.load("Holder.mp3")
os.remove("Speech.mp3")
And call it after .play(), so it now simply deletes the file when it is done and then re-creates it when you need to use it next.
To expand on my comment above (with help from this thread), I think play() may be locking the file. You can manually try the following:
def play():
mixer.init()
mixer.music.load("Speech.mp3")
mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(10)
or
def play():
mixer.init()
mixer.music.load("Speech.mp3")
mixer.music.play()
mixer.music.stop()
But this second fix might have the consequence of not hearing anything played back.
I fixed the problem by writing to a temporary file and using os.rename:
from gtts import gTTS
from pygame import mixer
import os
play_name = 'Speech.mp3'
save_name = "%s.%s" % (play_name, '.tmp')
def play():
mixer.music.load(play_name)
mixer.music.play()
def voice(x):
text = gTTS(text=x, lang='en')
with open(save_name, 'wb') as tmp_file:
text.write_to_fp(tmp_file)
os.rename(save_name, play_name)
try:
mixer.init()
while True:
voice_input = raw_input("What should Wellington Say: ")
voice(voice_input)
play()
except KeyboardInterrupt:
pass
I ran a test where I typed in a very long sentence and then another sentence while the first one was playing and everything still worked.

Categories

Resources