playing sound without initial click in python - python

I am new to python and am attempting to build a simple alarm app (command line for now). This is using Python 3.6 and I am developing on Ubuntu 18.04. When I play a sound using pydub, playsound or simpleaudio, the sound is preceded by an annoying click, which I presume is meant to emulate the pressing of a button on a machine. I may have missed it in the docs, but do not see anything.
To be clear, this clicking does not exist in the sound file. I have put the play command in a loop to verify and only hear it on the initial play. For example:
# run pydub
sound = AudioSegment.from_file(f, format="wav")
# play(sound)
for _ in range(2):
play(sound)
This happens regardless of playing wav, mp3 or flac.
FWIW - I have been unsuccessful using python-vlc and pygame. I fear spending much time only to continue to hear the "click".
So, the question is, how do I prevent the click or what library/module should I use to achieve playback of a snippet in such a simple app?

Probably the easiest way to eliminate click in existing audio data is to put a very short fade-in at the beginning to ensure playback starts from a "zero crossing"
sound = AudioSegment.from_file(f, format="wav")
# 10 ms fade in
sound = sound.fade_in(duration=10)
for _ in range(2):
play(sound)

I'm having the same issue on a MacBook Pro trying to play short sound clips with simpleaudio. If I play an array of zeros, I get an obnoxious click noise at the beginning and end as with this code. Built-in speakers, as well as air pods, show the same issue.
import simpleaudio as sa
import numpy as np
print("Play silence for 1 second.")
sample_rate = 44100
s = np.zeros(sample_rate).astype(np.int16)
sp = sa.play_buffer(s, 1, 1, sample_rate)
sp.wait_done()
print(" Done.")
[EDIT: Found my problem -- the second 1 in the arguments to play_buiffer() is the number of bytes per sample. It should have been 2 for a 16-bit sample size. But using 1 shouldn't break anything for this sample of zeros, but it does probably force the audio hardware to be reconfigured for 8-bit play vs 16 bit, and changing the configuration might well be the cause of the pop. When I switch to 2, there are no pops.]

Related

Is there a way to deliberately slow down audio being played through pygame? [duplicate]

quick question.
I'm running pygame under linux just to play some audio files.
I've got some .wav files and I'm having problems playing them back at the right speed.
import pygame.mixer, sys, time
#plays too fast
pygame.mixer.init(44100)
pygame.mixer.music.load(sys.argv[1])
pygame.mixer.music.play()
time.sleep(5)
pygame.mixer.quit()
#plays too slow
pygame.mixer.init(22100)
pygame.mixer.music.load(sys.argv[1])
pygame.mixer.music.play()
time.sleep(5)
pygame.mixer.quit()
I've ggogle code searched some stuff but everybody seems to be fine calling the init function with its default parameters. Can others try running this script and seeing if they get the same behavior or not? Does anybody know how to speed it up? Or adjust the speed for each file?
Thanks.
I had some mp3 audio tracks playing back slowed down. I updated the mixer frequency to be based on the mp3 sample rate using mutagen like so:
import pygame, mutagen.mp3
song_file = "your_music.mp3"
mp3 = mutagen.mp3.MP3(song_file)
pygame.mixer.init(frequency=mp3.info.sample_rate)
pygame.mixer.music.load(song_file)
pygame.mixer.music.play()
And it fixed the problem.
To improve Chris H answer. Here is a example of how to use the wave library.
import wave
import pygame
file_path = '/path/to/sound.wav'
file_wav = wave.open(file_path)
frequency = file_wav.getframerate()
pygame.mixer.init(frequency=frequency)
pygame.mixer.music.load(file_path)
pygame.mixer.music.play()
Remember that if you want to change frequency or any other parameter used in pygame.mixer.init you must call pygame.mixer.quit first. Pygame documentation
Open your audio file in a free audio tool like Audacity. It will tell you the sampling rate of your media. It will also allow you to convert to a different sampling rate so all your sounds can be the same.
I figured it out...
There is a wave module http://docs.python.org/library/wave.html and it can read the sample rate for wav files.
If you're using Ogg Vorbis (.ogg) encoding, the same problem of stuttering audio happens. You'll have to read the frequency of what you're trying to play before initializing the mixer object.
Here's how to play .ogg audio with appropriate frequency using pygame.
from pyogg import VorbisFile
from pygame import mixer
# path to your audio
path = "./file.ogg"
# an object representing the audio, see https://github.com/Zuzu-Typ/PyOgg
sound = VorbisFile(path)
# pull the frequency out of the Vorbis abstraction
frequency = sound.frequency
# initialize the mixer
mixer.init(frequency=frequency)
# add the audio to the mixer's music channel
mixer.music.load(path)
# mixer.music.set_volume(1.0)
# mixer.music.fadeout(15)
# play
mixer.music.play()

How to play an mp3 or wav file directly into my own microphone without it playing through my speakers? [duplicate]

Is there a way using python (and not any external software) to play a mp3 file like a microphone input?
For example, I have a mp3 file and with a python script it would play it through my mic so other in a voice room would hear it. As I say it is just an example.
Of course, I have done some research. I found out that I can use a software to create a virtual device and do few things to have the result. But my point is if it is possible without installing software but with some kind of python script?
It is possible but it isn't 100% in python as it requires the installation of other software. (Also from what I know this specific answer only works on Windows, but it should be similar on Linux with PulseAudio instead of VB-Audio Cable, but I'm not a daily Linux user so I don't know.)
First download: https://www.vb-audio.com/Cable/, this will create a "Virtual Audio Cable" where programs can play music to the input device (What looks like a speaker) and it'll pipe it to the output device (What looks like a microphone).
Then run this command in cmd: pip install pygame==2.0.0.dev8 (or py -m pip install pygame==2.0.0.dev8, depending on your installation of python) [Also the reason it's the dev version is that it requires some functions only in sdl2, whereas the main branch uses sdl1)
Then:
>>> from pygame._sdl2 import get_num_audio_devices, get_audio_device_name #Get playback device names
>>> from pygame import mixer #Playing sound
>>> mixer.init() #Initialize the mixer, this will allow the next command to work
>>> [get_audio_device_name(x, 0).decode() for x in range(get_num_audio_devices(0))] #Returns playback devices
['Headphones (Oculus Virtual Audio Device)', 'MONITOR (2- NVIDIA High Definition Audio)', 'Speakers (High Definition Audio Device)', 'Speakers (NVIDIA RTX Voice)', 'CABLE Input (VB-Audio Virtual Cable)']
>>> mixer.quit() #Quit the mixer as it's initialized on your main playback device
>>> mixer.init(devicename='CABLE Input (VB-Audio Virtual Cable)') #Initialize it with the correct device
>>> mixer.music.load("Megalovania.mp3") #Load the mp3
>>> mixer.music.play() #Play it
To stop the music do: mixer.music.stop()
Also, the music doesn't play through your speakers, so you're going to have another python script or thread running that handles playing it through your speakers. (Also if you want it to play on a button press I recommend using the python library keyboard, the GitHub documentation is really good and you should be able to figure it out on your own.)
PS: This took me a while to figure out, your welcome.
PPS: I'm still trying to figure out a way to pipe your own mic through there as well since this method will obviously not pipe your real microphone in too, but looking into the source code of pygame is making my head hurt due to it all being written in C.
If you meant how to play MP3 using Python, well, this is a broad question.
Is it possible, without any dependencies, yes it is, but it is not worth it. Well, playing uncompressed audio is, but MP3, well, I'll explain below.
To play raw audio data from Python without installing pyaudio or pygame or similar, you first have to know the platform on which your script will be run.
Then implement a nice set of functions for choosing an audio device, setting up properties like sample rate, bit rate, mono/stereo..., feeding the stream to audio card and stopping the playback.
It is not hard, but to do it you have to use ctypes on Windows, PyObjC on Mac and Linux is special case as it supports many audio systems (probably use sockets to connect to PulseAudio or pipe to some process like aplay/paplay/mpeg123... or exploit gstreamer.).
But why go through all this just to avoid dependencies, when you have nice libraries out there with simple interfaces to access and use audio devices.
PyAudio is great one.
Well, that is your concern.
But, playing MP3 without external libraries, in real time, from pure Python, well, it's not exactly impossible, but it is very hard to achieve, and as far as I know nobody even tried doing it.
There is pure Python MP3 decoder implementation, but it is 10 times slower than necessary for real-time audio playback. It can be optimized for nearly full speed, but nobody is interested in doing so.
It has mostly educational value and it is used in cases where you do not need real-time speed.
This is what you should do:
Install pygame and use it to play MP3 directly
or:
Install PyAudio and some library that decodes Mp3, there are quite a few of them on pypi.python.org, and use it to decode the MP3 and feed the output to PyAudio.
There are some more possibilities, including pymedia, but I consider these the easiest solutions.
Okay, as we clarified what is really you need here is the answer.
I will leave first answer intact as you need that part too.
Now, you want to play audio to the recording stream, so that any application recording the audio input records the stuff that you are playing.
On Windows, this is called stereo mix and can be found in Volume Control, under audio input.
You choose stereo mix as your default input. Now, when you open an recording app which doesn't select itsown input channel, but uses the selected one (e.g. Skype) , it will record all coming out of your speakers and coming into your mic/line in.
I am not 100% sure whether this option will appear on all Windows or it is a feature of an audio card you have.
I am positive that Creative and Realtek audio cards supports it.
So, research this.
To select that option from Python, you have to connect to winmm.dll using ctypes and call the appropriate function. I do not know which one and with what arguments.
If this option is not present in volume control, there is nothing for it but to install a virtual audio card to do the loopback for you.
There might be such a software that comes packaged in as library so that you can use it from Python or whatever.
On Linux this should be easy using Pulseaudio. I do not know how, but I know that you can do it, redirect the streams etc. There is tutorial out there somewhere.
Then you can call that command from Python, to set to this and reset back to normal.
On Mac, well, I really have no idea, but it should be possible.
If you want your MP3 to be played only to the recording stream, and not on your speakers at all, well on Windows, you will not be able to do that without a loopback audio device.
On Linux, I am sure you will be able to do it, and on Mac it should be possible, but how is the Q.
I currently have no time to sniff around libraries etc. to provide you with some useful code, so you will have to do it yourself. But I hope my directions will help you.
Just an update on #PyPylia's answer for the benefit of anyone who struggled to implement this like I did.
Current Package Version: pygame 2.1.2 (SDL 2.0.18, Python 3.10.6)
Tested Systems: Windows 10 (21H2 - 19044.1288), (Should be the same process on Mac but this is untested as of now...)
First, you'll need to download the VB-Cable Virtual Mic Driver for your respective platform and install it. This provides us with a virtual mic that'll allow us to pass audio we play on our machine as a microphone input when using a video calling software (Google Meet, Microsoft Teams, Zoom). After that, it's all handled through the pygame module's audio package.
To get the audio device list:
from pygame import mixer, _sdl2 as devicer
mixer.init() # Initialize the mixer, this will allow the next command to work
# Returns playback devices, Boolean value determines whether they are Input or Output devices.
print("Inputs:", devicer.audio.get_audio_device_names(True))
print("Outputs:", devicer.audio.get_audio_device_names(False))
mixer.quit() # Quit the mixer as it's initialized on your main playback device
For example, My device returns:
Inputs: ['Microphone (High Definition Audio Device)', 'CABLE Output (VB-Audio Virtual Cable)']
Outputs: ['Speakers (High Definition Audio Device)', 'CABLE Input (VB-Audio Virtual Cable)']
Then, to playback the audio:
import time
from pygame import mixer
mixer.init(devicename = 'CABLE Input (VB-Audio Virtual Cable)') # Initialize it with the correct device
mixer.music.load("Toby Fox - Megalovania.mp3") # Load the mp3
mixer.music.play() # Play it
while mixer.music.get_busy(): # wait for music to finish playing
time.sleep(1)
If you wish to play multiple tracks back to back, add the following code segments to the while loop above:
...
else:
mixer.music.unload() # Unload the mp3 to free up system resources
mixer.music.load("Sleeping at Last - Saturn.wav") # Load the wav
...
Then, on the other end, inside the relevant software, just change the microphone input from the default to CABLE Output (VB-Audio Virtual Cable) to have those on the other end hear the audio from the source.
If you're using a newer version of the package and some of the listed methods don't seem to work because of an AttributeError: module 'pygame' has no attribute {method_name}, use pyup and search for the method in question, to see if there have been any changes to how the method is invoked. This was the main reason #PyPylia's code snippet no longer works unless you use an older version of pygame.
If you want to play an audio file in local directory, you may follow this flow.
#!/usr/bin/env python
import pyaudio
import socket
import sys
import os
CHUNK = 4096
output = os.path.join(BASE_DIR, "speech.wav") #WAV format Output file name
wf = wave.open(output, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
try:
while True:
data = wf.readframes(CHUNK)
stream.write(data)
except KeyboardInterrupt:
pass
print('Shutting down')
s.close()
stream.close()
audio.terminate()

How to avoid Pygame changing female voice into male

I am trying to play an audio file of a female, speaking something in hindi using pygame library in python. When i manually click on audio file and listen to it, it is a female voice, but when but I play it via below script, I get a male voice. I guess it is converting female frequency into male. Why is that and how to avoid it?
Note: I am using Raspbian on Raspberry Pi.
This is the link to the audio file: https://drive.google.com/open?id=18pLBoCMxWZzB-RO3qqVmi0zREgJckb3M
My coding:
import pygame.time
from pygame.mixer import *
pre_init()
init()
filename = 'speech.wav'
music.load(filename)
music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(10)
import pygame.time
from pygame.mixer import *
pre_init()
init(frequency=32000)
filename = 'speech.wav'
music.load(filename)
music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(10)
pygame.mixer.init() takes a frequency parameter. And since the player defaults to 22050, the playback will be in slowm-otion - making it sound different. You can circumvent this by changing the speed manually, or you can probably get the actual speed from music.load(), the meta-data should be in the file.
Otherwise, just do ffmpeg -i speech.wav and you'll see the correct frequency at the bottom.
To change the frequency of audio-files to match the same frequencies, you could use ffmpeg to re-encode the files. Now, I'm no magician with ffmpeg - but the just goes something along the lines of:
ffmpeg -i speech.wav -af asetrate="32000*1.38125,atempo=1/1.38125" output.wav
Or use Audacity or something others recommend: https://superuser.com/questions/292833/how-to-change-audio-frequency
A second option to change frequency on the player instead, is to call pygame.mixer.quit() after each media file and re-initiate it with a new frequency matching your new file. Or lastly, read the docs and see if it's possible to change frequency playback on a already initialized instance of the mixer. This is beyond my knowledge tho. I just know what your original root problem is :)

Playing mp3 file through microphone with python

Is there a way using python (and not any external software) to play a mp3 file like a microphone input?
For example, I have a mp3 file and with a python script it would play it through my mic so other in a voice room would hear it. As I say it is just an example.
Of course, I have done some research. I found out that I can use a software to create a virtual device and do few things to have the result. But my point is if it is possible without installing software but with some kind of python script?
It is possible but it isn't 100% in python as it requires the installation of other software. (Also from what I know this specific answer only works on Windows, but it should be similar on Linux with PulseAudio instead of VB-Audio Cable, but I'm not a daily Linux user so I don't know.)
First download: https://www.vb-audio.com/Cable/, this will create a "Virtual Audio Cable" where programs can play music to the input device (What looks like a speaker) and it'll pipe it to the output device (What looks like a microphone).
Then run this command in cmd: pip install pygame==2.0.0.dev8 (or py -m pip install pygame==2.0.0.dev8, depending on your installation of python) [Also the reason it's the dev version is that it requires some functions only in sdl2, whereas the main branch uses sdl1)
Then:
>>> from pygame._sdl2 import get_num_audio_devices, get_audio_device_name #Get playback device names
>>> from pygame import mixer #Playing sound
>>> mixer.init() #Initialize the mixer, this will allow the next command to work
>>> [get_audio_device_name(x, 0).decode() for x in range(get_num_audio_devices(0))] #Returns playback devices
['Headphones (Oculus Virtual Audio Device)', 'MONITOR (2- NVIDIA High Definition Audio)', 'Speakers (High Definition Audio Device)', 'Speakers (NVIDIA RTX Voice)', 'CABLE Input (VB-Audio Virtual Cable)']
>>> mixer.quit() #Quit the mixer as it's initialized on your main playback device
>>> mixer.init(devicename='CABLE Input (VB-Audio Virtual Cable)') #Initialize it with the correct device
>>> mixer.music.load("Megalovania.mp3") #Load the mp3
>>> mixer.music.play() #Play it
To stop the music do: mixer.music.stop()
Also, the music doesn't play through your speakers, so you're going to have another python script or thread running that handles playing it through your speakers. (Also if you want it to play on a button press I recommend using the python library keyboard, the GitHub documentation is really good and you should be able to figure it out on your own.)
PS: This took me a while to figure out, your welcome.
PPS: I'm still trying to figure out a way to pipe your own mic through there as well since this method will obviously not pipe your real microphone in too, but looking into the source code of pygame is making my head hurt due to it all being written in C.
If you meant how to play MP3 using Python, well, this is a broad question.
Is it possible, without any dependencies, yes it is, but it is not worth it. Well, playing uncompressed audio is, but MP3, well, I'll explain below.
To play raw audio data from Python without installing pyaudio or pygame or similar, you first have to know the platform on which your script will be run.
Then implement a nice set of functions for choosing an audio device, setting up properties like sample rate, bit rate, mono/stereo..., feeding the stream to audio card and stopping the playback.
It is not hard, but to do it you have to use ctypes on Windows, PyObjC on Mac and Linux is special case as it supports many audio systems (probably use sockets to connect to PulseAudio or pipe to some process like aplay/paplay/mpeg123... or exploit gstreamer.).
But why go through all this just to avoid dependencies, when you have nice libraries out there with simple interfaces to access and use audio devices.
PyAudio is great one.
Well, that is your concern.
But, playing MP3 without external libraries, in real time, from pure Python, well, it's not exactly impossible, but it is very hard to achieve, and as far as I know nobody even tried doing it.
There is pure Python MP3 decoder implementation, but it is 10 times slower than necessary for real-time audio playback. It can be optimized for nearly full speed, but nobody is interested in doing so.
It has mostly educational value and it is used in cases where you do not need real-time speed.
This is what you should do:
Install pygame and use it to play MP3 directly
or:
Install PyAudio and some library that decodes Mp3, there are quite a few of them on pypi.python.org, and use it to decode the MP3 and feed the output to PyAudio.
There are some more possibilities, including pymedia, but I consider these the easiest solutions.
Okay, as we clarified what is really you need here is the answer.
I will leave first answer intact as you need that part too.
Now, you want to play audio to the recording stream, so that any application recording the audio input records the stuff that you are playing.
On Windows, this is called stereo mix and can be found in Volume Control, under audio input.
You choose stereo mix as your default input. Now, when you open an recording app which doesn't select itsown input channel, but uses the selected one (e.g. Skype) , it will record all coming out of your speakers and coming into your mic/line in.
I am not 100% sure whether this option will appear on all Windows or it is a feature of an audio card you have.
I am positive that Creative and Realtek audio cards supports it.
So, research this.
To select that option from Python, you have to connect to winmm.dll using ctypes and call the appropriate function. I do not know which one and with what arguments.
If this option is not present in volume control, there is nothing for it but to install a virtual audio card to do the loopback for you.
There might be such a software that comes packaged in as library so that you can use it from Python or whatever.
On Linux this should be easy using Pulseaudio. I do not know how, but I know that you can do it, redirect the streams etc. There is tutorial out there somewhere.
Then you can call that command from Python, to set to this and reset back to normal.
On Mac, well, I really have no idea, but it should be possible.
If you want your MP3 to be played only to the recording stream, and not on your speakers at all, well on Windows, you will not be able to do that without a loopback audio device.
On Linux, I am sure you will be able to do it, and on Mac it should be possible, but how is the Q.
I currently have no time to sniff around libraries etc. to provide you with some useful code, so you will have to do it yourself. But I hope my directions will help you.
Just an update on #PyPylia's answer for the benefit of anyone who struggled to implement this like I did.
Current Package Version: pygame 2.1.2 (SDL 2.0.18, Python 3.10.6)
Tested Systems: Windows 10 (21H2 - 19044.1288), (Should be the same process on Mac but this is untested as of now...)
First, you'll need to download the VB-Cable Virtual Mic Driver for your respective platform and install it. This provides us with a virtual mic that'll allow us to pass audio we play on our machine as a microphone input when using a video calling software (Google Meet, Microsoft Teams, Zoom). After that, it's all handled through the pygame module's audio package.
To get the audio device list:
from pygame import mixer, _sdl2 as devicer
mixer.init() # Initialize the mixer, this will allow the next command to work
# Returns playback devices, Boolean value determines whether they are Input or Output devices.
print("Inputs:", devicer.audio.get_audio_device_names(True))
print("Outputs:", devicer.audio.get_audio_device_names(False))
mixer.quit() # Quit the mixer as it's initialized on your main playback device
For example, My device returns:
Inputs: ['Microphone (High Definition Audio Device)', 'CABLE Output (VB-Audio Virtual Cable)']
Outputs: ['Speakers (High Definition Audio Device)', 'CABLE Input (VB-Audio Virtual Cable)']
Then, to playback the audio:
import time
from pygame import mixer
mixer.init(devicename = 'CABLE Input (VB-Audio Virtual Cable)') # Initialize it with the correct device
mixer.music.load("Toby Fox - Megalovania.mp3") # Load the mp3
mixer.music.play() # Play it
while mixer.music.get_busy(): # wait for music to finish playing
time.sleep(1)
If you wish to play multiple tracks back to back, add the following code segments to the while loop above:
...
else:
mixer.music.unload() # Unload the mp3 to free up system resources
mixer.music.load("Sleeping at Last - Saturn.wav") # Load the wav
...
Then, on the other end, inside the relevant software, just change the microphone input from the default to CABLE Output (VB-Audio Virtual Cable) to have those on the other end hear the audio from the source.
If you're using a newer version of the package and some of the listed methods don't seem to work because of an AttributeError: module 'pygame' has no attribute {method_name}, use pyup and search for the method in question, to see if there have been any changes to how the method is invoked. This was the main reason #PyPylia's code snippet no longer works unless you use an older version of pygame.
If you want to play an audio file in local directory, you may follow this flow.
#!/usr/bin/env python
import pyaudio
import socket
import sys
import os
CHUNK = 4096
output = os.path.join(BASE_DIR, "speech.wav") #WAV format Output file name
wf = wave.open(output, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
try:
while True:
data = wf.readframes(CHUNK)
stream.write(data)
except KeyboardInterrupt:
pass
print('Shutting down')
s.close()
stream.close()
audio.terminate()

Can't get winsound.Beep to work

I am trying to make some Beep noises with the winsound.Beep command. However when I am using winsound.Beep(500, 500) I do not hear anything. With winsound.MessageBeep and winsound.PlaySound however, I DO get to play sounds. Any ideas what I should do?
What I am trying to do: I want to write a little practicing program for training intervals: the computer sounds a first tone, then a second tone and you will have to guess what the tone interval is. For this I need pitched tones, tones for which I can set the frequency. I want to keep it as simple as possible, any pitched sound will do. I do not want to have to collect a set of .wav files or whatever. I want to make use of a tone generator which I think is available on most soundcards. winsound.Beep seems like something that can do this trick, but any other suggestions are welcome.
I had the exact same problem. winsound.Beep used to work just fine and then it suddenly stopped working (or that's what I thought). The problem was that someone (or some update) had turned off the System sounds, which prevented windows from playing the Beep sound, either manually or through my program. Try right clicking on the Speaker symbol, Open volume mixer and check whether System sounds is off or minimum volume.
I hope that helps!
Are you sure that your computer has a beep? A lot of recent computer remove the beep because it was annoying and most computer today have soundcard to play wav sound instead (the other sound that you are able to play).
You can also check to validate if it's activated on this page
IMO, I think that using the beep for other things than debugging is not a good idea.
EDIT
Mayby you can try this code to create a sound using a base wav with synth algorythm

Categories

Resources