How can I change the volume of a stream playing in pyaudio - python

I have a normal stream in Pyaudio that plays a wav file but I can not find any way to change the volume of the stream. I tried multiplying the data by some number but that did not work.
import pyaudio
import wave
audio = pyaudio.PyAudio()
chunk = 1024
af = wave.open("filename.wav", 'rb')
pa = pyaudio.PyAudio()
stream = pa.open(format =
pa.get_format_from_width(af.getsampwidth()),
channels = af.getnchannels(),
rate = af.getframerate(),
output_device_index=9,
output = True)
rd_data = af.readframes(chunk)
while rd_data != '':
#do some majic to change the volume
stream.write(rd_data)
rd_data = af.readframes(chunk)

You can use numpy for this:
import numpy
def audio_datalist_set_volume(datalist, volume):
""" Change value of list of audio chunks """
sound_level = (volume / 100.)
for i in range(len(datalist)):
chunk = numpy.fromstring(datalist[i], numpy.int16)
chunk = chunk * sound_level
datalist[i] = chunk.astype(numpy.int16)

Related

Play wav file in callback mode [Pyaudio / Python]

I wanna play wav file using PyAudio with callback mode. I could play wav file once but then stops. For example, if I want to play a wav file (trumpet sound, etc.) every five seconds, how should I implement this? Also, lemme know how to play array (wav data) in callback mode, just in case.
The code that I've implemented so far in the following...
import pyaudio
import numpy as np
import wave
import time
FORMAT = pyaudio.paInt16
CHANNEL = 2
CHUNK = 1024
RATE = 44100
flag = True
cur_dt = 0
# reading wav file here
wf = wave.open('test_3.wav', 'rb')
# instantiate PyAudio (1)
p = pyaudio.PyAudio()
# define callback (2)
def callback(in_data, frame_count, time_info, status):
global flag, cur_dt, wf
if flag:
wf = wave.open('test_3.wav', 'rb')
flag = False
data = np.zeros((frame_count,), dtype=np.int16)
data = wf.readframes(frame_count)
# convert bytes to int
data_int = np.frombuffer(data, dtype=np.int16)\
# convert int to float
data_float = data_int.astype(np.float32) / 32768.0
# # convert float to int
# data_int_again = (data_float*32768.0).astype(np.int16, order='C')
# ur processing...
f1 = np.fft.fft(data_float)
f2 = np.fft.ifft(f1)
f3 = (f2*32768.0).astype(np.int16)
# make it sound!
data = f3
# cur_dt += 1
# print(cur_dt)
return (data.tobytes(), pyaudio.paContinue)
# open stream using callback (3)
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
stream_callback=callback)
# print(p.get_format_from_width(wf.getsampwidth())) # paInt16
# print(wf.getsampwidth()) # 3
# print(wf.getnchannels()) # 2
# print(wf.getframerate()) # 44100
stream.start_stream()
# wait for stream to finish (5)
while stream.is_active():
time.sleep(5)
flag = True
# stop stream (6)
stream.stop_stream()
stream.close()
wf.close()
# close PyAudio (7)
p.terminate()

Opening wav from URL in Python

With the Python script shown below I try to play a wav file from the internet but I'm getting the error message OSError: [Errno 22] Invalid argument: 'https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav'.
How can I play a wav file from the internet?
import pyaudio
import wave
chunk = 1024
f = wave.open("https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav","rb")
p = pyaudio.PyAudio()
stream = p.open(format = p.get_format_from_width(f.getsampwidth()),
channels = f.getnchannels(),
rate = f.getframerate(),
output = True)
data = f.readframes(chunk)
while data:
stream.write(data)
data = f.readframes(chunk)
stream.stop_stream()
stream.close()
p.terminate()
You can also get the content of website, store it in a variable, and play it. There is no need to store it on the disk for a short file like this. Here is an example of how to do this:
import logging
import requests
import simpleaudio
sample_rate = 8000
num_channels = 2
bytes_per_sample = 2
total = sample_rate * num_channels * bytes_per_sample
logging.basicConfig(level=logging.INFO)
audio_url = "https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav"
logging.info(f"Downloading audio file from: {audio_url}")
content = requests.get(audio_url).content
# Just to ensure that the file does not have extra bytes
blocks = len(content) // total
content = content[:total * blocks]
wave = simpleaudio.WaveObject(audio_data=content,
sample_rate=sample_rate,
num_channels=num_channels,
bytes_per_sample=bytes_per_sample)
control = wave.play()
control.wait_done()
I'm demonstrating what #larsks suggests.
import requests
with open(audio_file, 'wb') as a:
resp = requests.get("https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_1MG.wav")
if resp.status_code == 200:
a.write(resp.content)
print('downloaded')
else:
print(resp.reason)
exit(1)
f = wave.open(audio_file, "rb")
# the remaining lines are the same
And I also suggest another great python library python-mpv which is based on mpv, this library can handle much more codecs and also online streaming play.

Python - Record on loop, stop recording when silent

I am trying to write a Python script that will record 5-second segments of speech on a loop for as long as the user is speaking, and will stop after three wave files of pure silence. How would I go about this?
import pyaudio
import wave
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = CHUNK)
print("* recording")
frames = []
for j in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
print("* done recording")
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
I think you can use https://github.com/rhasspy/rhasspy-silence library
Here is a little code for u (not mine, but it works.):
from rhasspysilence import WebRtcVadRecorder,VoiceCommand, VoiceCommandResult
import threading
import dataclasses
import typing
from queue import Queue
import json
import io
import os
from pathlib import Path
import shlex
import time
import wave
import sys
import subprocess
import pyaudio
pa =pyaudio.PyAudio()
#you can change the options (these are default settings)
vad_mode = 3
sample_rate = 16000
min_seconds= 1
max_seconds = 30
speech_seconds = 0.3
silence_seconds = 0.5
before_seconds = 0.2
chunk_size= 960
skip_seconds = 0
audio_source = None
channels =1
def SpeechToText():
recorder = WebRtcVadRecorder(vad_mode=vad_mode,)
recorder.start()
# file directory
wav_sink = 'wavs/'
wav_dir = None
# file name
wav_filename = 'tester'
if wav_sink:
wav_sink_path = Path(wav_sink)
if wav_sink_path.is_dir():
# Directory to write WAV files
wav_dir = wav_sink_path
else:
# Single WAV file to write
wav_sink = open(wav_sink, "wb")
voice_command: typing.Optional[VoiceCommand] = None
audio_source = pa.open(rate=sample_rate,format=pyaudio.paInt16,channels=channels,input=True,frames_per_buffer=chunk_size)
audio_source.start_stream()
print("Ready", file=sys.stderr)
def buffer_to_wav(buffer: bytes) -> bytes:
"""Wraps a buffer of raw audio data in a WAV"""
rate = int(sample_rate)
width = int(2)
channels = int(1)
with io.BytesIO() as wav_buffer:
wav_file: wave.Wave_write = wave.open(wav_buffer, mode="wb")
with wav_file:
wav_file.setframerate(rate)
wav_file.setsampwidth(width)
wav_file.setnchannels(channels)
wav_file.writeframesraw(buffer)
return wav_buffer.getvalue()
try:
chunk = audio_source.read(chunk_size)
while chunk:
# Look for speech/silence
voice_command = recorder.process_chunk(chunk)
if voice_command:
is_timeout = voice_command.result == VoiceCommandResult.FAILURE
# Reset
audio_data = recorder.stop()
if wav_dir:
# Write WAV to directory
wav_path = (wav_dir / time.strftime(wav_filename)).with_suffix(
".wav"
)
wav_bytes = buffer_to_wav(audio_data)
wav_path.write_bytes(wav_bytes)
print(wav_path)
print('file saved')
break
elif wav_sink:
# Write to WAV file
wav_bytes = core.buffer_to_wav(audio_data)
wav_sink.write(wav_bytes)
# Next audio chunk
chunk = audio_source.read(chunk_size)
finally:
try:
audio_source.close_stream()
except Exception:
pass
#execute command
SpeechToText()

Recording and playing byte list in pyaudio/wave

I want to record and play my voice using pyaudio and wave lib but I don't know how to do it because wave lib requires a path to a file and even if I'm trying to set it as a variable with list of bytes recorded a few second ago, still doesn't work beacuse I can't use 'read' for a list. Does someone have some idea? I want to make a looper like KORG stuff, etc
I want to play it immediately after stopped recording, like real looper, without saving record as file.
There is my code (Python 3.4):
def record(self): #recording a voice
#var for bytes from recording
self.stream = self.player.open(format = self.FORMAT,
channels = self.CHANNELS,
rate = self.RATE,
input = True,
frames_per_buffer = self.CHUNK)
print("Recording")
self.frames = [] #byte list
#recoring for a few seconds (5sec at this moment)
for i in range(0, int(self.RATE / self.CHUNK * self.RECORD_SECONDS)):
self.data = self.stream.read(self.CHUNK) #sing stream do data var
self.frames.append(self.data) #add bytes to the end of a list
print("Stop recording")
self.stopRecording()
def stopRecording(self):
self.stream.stop_stream()
self.stream.close()
print("Recording has been stopped")
self.play()
def play(self): #playing a record
print("Playing")
f = wave.open(self.frames,"rb")
#read data
data = f.readframes(CHUNK)
#play stream
while data != '':
self.stream.write(data)
data = f.readframes(CHUNK)
self.stopPlaying()
After stop your record you need join your appended data, use data = ''.join(self.frames), and at the end build a loop (for, while) to stream all your byte list, here is how i did:
import pyaudio
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 41000
RECORD_SECONDS = 5
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)
print ("***Recording***")
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
data = stream.read(chunk)
all.append(data)
print("***Stop recording***")
print ("***START PLAY***")
data = ''.join(all)
for i in range(0, len(data), chunk):
stream.write(data[i:i+chunk])

Playing a single channel of audio using PyAudio or similar

I currently have a 2 channel wav file. I would like to have the ability to play one channel at a time. Using the examples from the PyAudio website where you read in a chunk of data through the wave module and then write to stream, I am unsure whether I need to somehow read in one channel of data and write to a one channel stream, or if there is some other solution. Thanks for the help.
I propose you alter the stream and overwrite the channel that you do not want to hear, with data from the channel you want to hear. Normally, channels are interleaved, so the following should do the trick.
import pyaudio
import wave
import sys
chunk = 1024
def convert(data, sampleSize = 4, channel = 0):
for i in range(0, len(data), 2*sampleSize):
for j in range(0, sampleSize):
data[i + j + sampleSize * channel] = data[i + j + sampleSize * (1 - channel)]
if len(sys.argv) < 2:
print "Plays a wave file.\n\n" +\
"Usage: %s filename.wav" % sys.argv[0]
sys.exit(-1)
wf = wave.open(sys.argv[1], 'rb')
p = pyaudio.PyAudio()
# open stream
stream = p.open(format =
p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True)
# read data
data = wf.readframes(chunk)
convert(data)
# play stream
while data != '':
stream.write(data)
data = wf.readframes(chunk)
convert(data)
stream.close()
p.terminate()

Categories

Resources