How to play a sound onto a input stream - python

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?).

Related

X310 USRP with python

I'm new to USRP so I don't quite understand the transmission and reception. I have a IQ data needs to be transmitted, I'm using the tx_waveform.py to perform the transmission or any other I should use? What would be the output of this? What are the configuration need to be done to transmit the data and receive the IQ data on receiver (OTA or Over the wire)?? What should be the procedure of this? I have attached the example code I found, this would come in receiver part, but how to use the tx_waveform.py? I just need to have a simple end to end transmission setup.
Thank you
I am using the same X310 USRP for the close loop transmission by simply connect a RF cable between them. Besides, I have one octoclock is connected to this USRP.
num_symbols = 10000
r = 0.1*np.random.randn(num_symbols) + 0.1j*np.random.randn(num_symbols)
print(r.size)
r = r.astype(np.complex64) # Convert to 64
r.tofile('test.dat')
import uhd
import numpy as np
usrp = uhd.usrp.MultiUSRP()
num_samps = 10000
center_freq = 1e9
sample_rate = 50e6
gain = 20
usrp.set_rx_rate(sample_rate, 0)
usrp.set_rx_freq(uhd.libpyuhd.types.tune_request(center_freq), 0)
usrp.set_rx_gain(gain, 0)
# Set up the stream and receive buffer
st_args = uhd.usrp.StreamArgs("fc32", "sc16")
st_args.channels = [0]
metadata = uhd.types.RXMetadata()
streamer = usrp.get_rx_stream(st_args)
recv_buffer = np.zeros((1, 1000), dtype=np.complex64) #maximum allowed value is 1996 through streamer.get_max_num_samps()
# Start Stream
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.start_cont)
stream_cmd.stream_now = True
streamer.issue_stream_cmd(stream_cmd)
# Receive Samples
samples = np.fromfile('test.dat', np.complex64)
for i in range(num_samps//1000):
streamer.recv(recv_buffer, metadata)
samples[i*1000:(i+1)*1000] = recv_buffer[0]
# Stop Stream
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.stop_cont)
streamer.issue_stream_cmd(stream_cmd)
print(samples)
Your code looks like it was taken from this source with a few minor modifications: PySDR: A Guide to SDR and DSP using Python
If you look a bit further down on that page, there is an example describing how to transmit data, using the usrp.send_waveform()
Furthermore, the modification you made to that example is a bit strange:
# Receive Samples
samples = np.fromfile('test.dat', np.complex64) #<-- here you populate samples with data from your file
for i in range(num_samps//1000):
streamer.recv(recv_buffer, metadata)
samples[i*1000:(i+1)*1000] = recv_buffer[0] #<-- now you overwrite with received data
First you need to confirm you have uhd package by runing:
python
import uhd
Then you need to confirm your host PC could find your USRP X310 by typing
uhd_find_devices --args addr={your X310''s IP address}
Then you could download latest python examples [tx_waveforms.py,rx_to_file.py] from uhd's repo
Then you could run --help to see if you can understand how to use these two file
python
tx_waveforms.py --help
rx_to_file.py --help
Also it will make you know that you are missing some packages which you need to pip install by yourself.
This page will inspire you how to pass the argument to the program even though it was not using the same file as you downloaded, but the low level API is similar.

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.

How to read realtime microphone audio volume in python and ffmpeg or similar

I'm trying to read, in near-realtime, the volume coming from the audio of a USB microphone in Python.
I have the pieces, but can't figure out how to put it together.
If I already have a .wav file, I can pretty simply read it using wavefile:
from wavefile import WaveReader
with WaveReader("/Users/rmartin/audio.wav") as r:
for data in r.read_iter(size=512):
left_channel = data[0]
volume = np.linalg.norm(left_channel)
print volume
This works great, but I want to process the audio from the microphone in real-time, not from a file.
So my thought was to use something like ffmpeg to PIPE the real-time output into WaveReader, but my Byte knowledge is somewhat lacking.
import subprocess
import numpy as np
command = ["/usr/local/bin/ffmpeg",
'-f', 'avfoundation',
'-i', ':2',
'-t', '5',
'-ar', '11025',
'-ac', '1',
'-acodec','aac', '-']
pipe = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=10**8)
stdout_data = pipe.stdout.read()
audio_array = np.fromstring(stdout_data, dtype="int16")
print audio_array
That looks pretty, but it doesn't do much. It fails with a [NULL # 0x7ff640016600] Unable to find a suitable output format for 'pipe:' error.
I assume this is a fairly simple thing to do given that I only need to check the audio for volume levels.
Anyone know how to accomplish this simply? FFMPEG isn't a requirement, but it does need to work on OSX & Linux.
Thanks to #Matthias for the suggestion to use the sounddevice module. It's exactly what I need.
For posterity, here is a working example that prints real-time audio levels to the shell:
# Print out realtime audio volume as ascii bars
import sounddevice as sd
import numpy as np
def print_sound(indata, outdata, frames, time, status):
volume_norm = np.linalg.norm(indata)*10
print ("|" * int(volume_norm))
with sd.Stream(callback=print_sound):
sd.sleep(10000)
Python 3 user here
I had few problems to make that work so I used:
https://python-sounddevice.readthedocs.io/en/0.3.3/examples.html#plot-microphone-signal-s-in-real-time
And I need to install sudo apt-get install python3-tk for python 3.6 look Tkinter module not found on Ubuntu
Then I modified script:
#!/usr/bin/env python3
import numpy as np
import sounddevice as sd
duration = 10 #in seconds
def audio_callback(indata, frames, time, status):
volume_norm = np.linalg.norm(indata) * 10
print("|" * int(volume_norm))
stream = sd.InputStream(callback=audio_callback)
with stream:
sd.sleep(duration * 1000)
And yes it working :)

Real-time procedural sounds with 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

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