Real-time procedural sounds with Python? - python

I want to do procedural sounds in Python, and instantly play them back rather than save them to a file. What should I be using for this? Can I just use built-in modules, or will I need anything extra?
I will probably want to change pitch, volume, things like that.

Using numpy along with scikits.audiolab should do the trick. audiolab has a play function which supports the ALSA and Core Audio backends.
Here's an example of how you might produce a simple sine wave using numpy:
from __future__ import division
import numpy as np
def testsignal(hz,amplitude = .5,seconds=5.,sr=44100.):
'''
Create a sine wave at hz for n seconds
'''
# cycles per sample
cps = hz / sr
# total samples
ts = seconds * sr
return amplitude * np.sin(np.arange(0,ts*cps,cps) * (2*np.pi))
To create five seconds of a sine wave at 440 hz and listen to it, you'd do:
>>> from scikits.audiolab import play
>>> samples = testsignal(440)
>>> play(samples)
Note that play is a blocking call. Control won't be returned to your code until the sound has completed playing.

Check out this Python wiki page. Particularly the "Music programming in Python" section.

Pyglet could be a good option as it embeds functions for audio procedural:
Pyglet/media_procedural

Related

Librosa adds noise to wav data

I was programming a little something reading a file and playing it back. I need it to use librosa, it it's impossible i might be able to fix it. the simpleaudio bit is easier to replace. This is my code:
from pathlib import Path
import librosa
import simpleaudio as sa
def play_audio(audio, sampling_rate):
print("PLAYING AUDIO")
wave_obj = sa.WaveObject(audio, sample_rate=sampling_rate)
play_obj = wave_obj.play()
play_obj.wait_done()
in_fpath = Path("trump.wav")
original_wav, sampling_rate = librosa.load(in_fpath)
play_audio(original_wav, int(sampling_rate))
And it loads the data and plays it back, but it's just that if I play trump.wav file in Windows on the music player, it sounds like it should. When I do it in Python this way, however, it becomes EXTREMELY noisy. You can still hear what he is saying, but barely. Where is the problem? Librosa or SimpleAudio?
I have a suspicion. librosa.load returns an array with float data but simpleaudio needs integer data. Please try to change the dtype of audio:
import numpy as np
# [...]
audio *= 32767 / np.max(np.abs(audio)) # re-scaling
audio = audio.astype(np.int16) # change data type
wave_obj = sa.WaveObject(audio, sample_rate=sampling_rate)
# [...]
Also see the documentation of simpleaudio:
https://simpleaudio.readthedocs.io/en/latest/tutorial.html#using-numpy
I figured out the problem, i just needed to change the WaveObject or librosa.load (don't remember which) with a byte_rate (something with byte_) to 4 instead of the default of 2.

Playing audio in jupyter, in a for loop

I have a ton of training data I need annotated, in order to do so I need to listen through a bunch of sound snippets and note what I hear. I wrote a small script for this in a notebook.
My main issue is that IPython display dosent show in loops. As an example:
import numpy
import IPython.display as ipd
sr = 22050# sample rate
T = 2.0# seconds
t = numpy.linspace(0, T, int(T*sr), endpoint=False)# time variable
x = 0.5*numpy.sin(2*numpy.pi*440*t)
ipd.Audio(x, rate=sr)
will show up with an audio box, and I will be able to play the sine wave.
But trying to play anything in a for loop yields nothing (such as:)
for i in range(10000000):
x = 0.5*numpy.sin(i*numpy.pi*440*t)
ipd.Audio(x, rate=sr)
If anyone has a good solution for looping through (and listening) a bunch of audio files (one at a time, since I need to loop through potentially hundreds of thousands sound snippets), I would be very much appreciative!
To display the audio files within the for loop, you need to use IPython.display.display with the Audio object like so:
import numpy
import IPython.display as ipd
for i in range(10000000):
x = 0.5*numpy.sin(i*numpy.pi*440*t)
ipd.display(ipd.Audio(x, rate=sr))
my answer was deleted. but if you want a continuous loop you can use the method i described here https://stackoverflow.com/a/73425194/664456
which is a = Audio(...); a.autoplay_attr = lambda: 'autoplay="autoplay" loop="loop"'

Python playTone(freq, duration) command for Learning and Teaching

I'm really baffled by this. I asked a few years back how to generate a tone with Python using something like playTone(freq, duration) and was overwhelmed by the complexity of the responses, and the lack of any simple solution.
The winsound module is inadequate as it trips over itself after a few notes, and is too limited in other ways (maybe 2 notes at a time would be nice, or a choice of wave types).
As a teacher, it makes a huge amount of sense to me to use sound to illustrate programming principles such as loops and randomness, as many people are auditory learners.
This kind of thing was incredibly simple with early home computers running Basic, where you could create 4 channel master pieces with ease. I just don't understand why such a thing isn't easily available with Python, which is meant to be a so well suited to learners.
JavaScript now can do this relatively easily with the audio context and its oscillators, but I want to use Python, and in particular to combine the visual power and simplicity of turtle graphics with an auditory component that doesn't require in depth knowledge of computer hardware and physics to produce.
Can anyone help me to find a simple up-to-date (late 2016) solution for this please?
There are various ways to do it. Here is an simple implementation using pyaudio module.
You can install pyaudio using
pip install pyaudio #for windows and
sudo apt-get install python-pyaudio #For linux
Following program has playTone function that gets two inputs; frequency and duarion. You can vary those to get the desired audible frequency tones and "bearable" duration.
If you are planning to create multi-channel complex audio , then you may want to look into pygame. There are ample examples on SO for that.
import pyaudio
import math
def playTone( freq , length):
bit_rate = 16000 #number of frames per second/frameset.
frequency = freq #in Hz, waves per second
play_time = length #in seconds to play sound
if frequency > bit_rate:
bit_rate = frequency+100
num_frames = int(bit_rate * play_time)
total_frames = num_frames % bit_rate
wave_info = ''
for x in xrange(num_frames):
wave_info = wave_info+chr(int(math.sin(x/((bit_rate/frequency)/math.pi))*127+128))
for x in xrange(total_frames):
wave_info = wave_info+chr(128)
p = PyAudio()
stream = p.open(format = p.get_format_from_width(1),
channels = 1,
rate = bit_rate,
output = True)
stream.write(wave_info)
stream.stop_stream()
stream.close()
p.terminate()
if __name__ == '__main__':
frequency = 1500 #Hz
duration = 2 #seconds
PyAudio = pyaudio.PyAudio
#Function to play frequency for given duration
playTone(frequency , duration)

How to play a sound onto a input stream

I was wondering if it was possible to play a sound directly into a input from python. I am using linux, and with that I am using OSS, ALSA, and Pulseaudio
You can definitely play (and generate) sound with python
Here is a example code that generates sinewave, opens default Alsa playback device and plays sinewave through that
#!/usr/bin/env python3
import math
import struct
import alsaaudio
from itertools import *
def sine_wave(frequency=440.0, framerate=44100, amplitude=0.5):
"""Stolen from here: http://zacharydenton.com/generate-audio-with-python/"""
period = int(framerate / frequency)
if amplitude > 1.0: amplitude = 1.0
if amplitude < 0.0: amplitude = 0.0
lookup_table = [float(amplitude) * math.sin(2.0*math.pi*float(frequency)*(float(i%period)/float(framerate))) for i in range(period)]
return (lookup_table[i%period] for i in count(0))
sound_out = alsaaudio.PCM() # open default sound output
sound_out.setchannels(1) # use only one channel of audio (aka mono)
sound_out.setrate(44100) # how many samples per second
sound_out.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE) # sample format
for sample in sine_wave():
# alsa only eats binnary data
b = struct.pack("<f", sample) # convert python float to binary float
sound_out.write(b)
or you can loopback microphone to your speakers
#!/usr/bin/env python3
import struct
import alsaaudio
sound_out = alsaaudio.PCM() # open default sound output
sound_out.setchannels(1) # use only one channel of audio (aka mono)
sound_out.setperiodsize(5) # buffer size, default is 32
sound_in = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE) # default recording device
sound_in.setchannels(1) # use only one channel of audio (aka mono)
sound_in.setperiodsize(5) # buffer size, default is 32
while True:
sample_lenght, sample = sound_in.read()
sound_out.write(sample)
much more examples can be found in python alsaaudio library http://pyalsaaudio.sourceforge.net/libalsaaudio.html
I guess it depends on what you would like to do with it after you got it "into" python.
I would definitely look at the scikits.audiolab library. That's what you might use if you wanted to draw up spectrograms of what ever sound you are trying process (I'm guessing that's what you want to do?).

Python: what are the nearest Linux and OS X [edit: macOS] equivalents of winsound.Beep?

If one wishes to beep the speaker on Windows, Python 2 apparently provides a useful function: winsound.Beep(). The neat thing about this function is that it takes arguments specifying the exact frequency and duration of the beep. This is exactly what I want to do, except that I don't use Windows. So...
What are the nearest equivalents of winsound.Beep() for Linux and OS X [edit: macOS], bringing in as few dependencies as possible?
Please note that I want to be able to beep the speaker directly, not to play a sound file. Also, I need to be able to control the frequency and duration of the beep, so curses.beep() and print '\a' won't do. Lastly, I am aware that PyGame provides extensive sound capabilities, but given that I don't require any of PyGame's other functionality, that would seem like using a sledgehammer to crack a nut (and anyway, I'm trying to do away with dependencies as far as possible).
[Edited on 9 Feb 2023 to reflect the fact that OS X was renamed macOS a few years after this question was asked]
winsound is only for windows and I could not find any cross platform way to do this, other than print "/a". However, you cannot set the frequency and duration with this.
However, you can try the os.system command to do the same with the system command beep. Here is a snippet, which defines the function playsound in a platform independent way
try:
import winsound
except ImportError:
import os
def playsound(frequency,duration):
#apt-get install beep
os.system('beep -f %s -l %s' % (frequency,duration))
else:
def playsound(frequency,duration):
winsound.Beep(frequency,duration)
For more info, look at this blog
EDIT: You will need to install the beep package on linux to run the beep command. You can install by giving the command
sudo apt-get install beep
I found a potential solution here:
http://bytes.com/topic/python/answers/25217-beeping-under-linux
It involves writing directly to /dev/audio. Not sure how portable it is or if it even works at all - i'm not on a linux machine atm.
def beep(frequency, amplitude, duration):
sample = 8000
half_period = int(sample/frequency/2)
beep = chr(amplitude)*half_period+chr(0)*half_period
beep *= int(duration*frequency)
audio = file('/dev/audio', 'wb')
audio.write(beep)
audio.close()
This works on mac:
import numpy as np
import simpleaudio as sa
def sound(x,z):
frequency = x # Our played note will be 440 Hz
fs = 44100 # 44100 samples per second
seconds = z # Note duration of 3 seconds
# Generate array with seconds*sample_rate steps, ranging between 0 and seconds
t = np.linspace(0, seconds, seconds * fs, False)
# Generate a 440 Hz sine wave
note = np.sin(frequency * t * 2 * np.pi)
# Ensure that highest value is in 16-bit range
audio = note * (2**15 - 1) / np.max(np.abs(note))
# Convert to 16-bit data
audio = audio.astype(np.int16)
# Start playback
play_obj = sa.play_buffer(audio, 1, 2, fs)
# Wait for playback to finish before exiting
play_obj.wait_done()
sound(300,2)
sound(200,1)
The most light-weight cross-platform layer I can see is "PortAudio". This is used by R for instance in their package to wrap platform-specific driver calls into simple play/record of digitized waveforms as an array.
The good folk at M.I.T. produce a Python binding for this, but you will need to include the compiled .dll/.so for this to work. http://people.csail.mit.edu/hubert/pyaudio/
( libao is similar by Xiph the makers of Ogg/Vorbis , a wrapper pyao exists but this seems less widely used )
SoX is an excellent set of cross-platform tools with much more functionality for format conversion and reading files etc..
Using ctypes to make calls from Python to a driver is feasible but very messy, even the simplest legacy WinMM.
I've found 3 methods for Linux:
new method using the Linux evdev API, works with any user in the input group (example source code)
old method using fcntl and /dev/console (requires root priviledges) (example source code)
invoke the beep command directly with subprocess or os.system (slower and must be installed in the system).
See also my tone() function here with all the alternatives.

Categories

Resources