The Qt documentation (https://doc.qt.io/qtforpython-5/PySide2/QtMultimedia/QAudioBuffer.html) says that we should read the buffer from QAudioProbe like this:
// With a 16bit sample buffer:
quint16 *data = buffer->data<quint16>(); // May cause deep copy
This is C++, but I need to write this in Python.
I am not sure how to use the Qt quint16 data type or even how to import it.
Here is my full code:
#!/bin/python3
from PySide2.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe, QAudioBuffer
from PySide2.QtCore import QUrl, QCoreApplication, QObject, Signal, Slot
import sys
def main():
app = QCoreApplication()
player = QMediaPlayer()
url = QUrl.fromLocalFile("/home/ubuntu/sound.wav")
content = QMediaContent(url)
player.setMedia(content)
player.setVolume(50)
probe = QAudioProbe()
probe.setSource(player)
probe.audioBufferProbed.connect(processProbe)
player.play()
def processProbe(probe):
print(probe.data())
if __name__ == "__main__":
main()
Output:
shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
...
I ran into the same issue with a fresh PySide2 5.13.2 environment, and running print(probe.data().toBytes()) returned chunks of size 0 which I knew couldn't be the case because other built-in functionality was accessing the data.
I hate this hack as much as anyone else, but if you want to test things it is possible to access the buffer contents this way (please do not use this in production code):
Find out about the datatype, endian-ness etc of your buffer via format, and infer the proper C type that you'll need (e.g. signed int 16).
Extract the printed address from the VoidPtr printout, and convert it to an integer
Create a numpy array by reading at the given address, with the given type, and by the given amount of frames.
Code:
First of all, somewhere in your app, you'll be connecting your QAudioProbe to your source via setSource, and then the audioBufferProbed signal to a method e.g.:
self.audio_probe.audioBufferProbed.connect(self.on_audio_probed)
Then, the following on_audio_probed functionality will fetch the numpy array and print its norm, which should increase in presence of sound:
import numpy as np
import ctypes
def get_buffer_info(buf):
"""
"""
num_bytes = buf.byteCount()
num_frames = buf.frameCount()
#
fmt = buf.format()
sample_type = fmt.sampleType() # float, int, uint
bytes_per_frame = fmt.bytesPerFrame()
sample_rate = fmt.sampleRate()
#
if sample_type == fmt.Float and bytes_per_frame == 4:
dtype = np.float32
ctype = ctypes.c_float
elif sample_type == fmt.SignedInt and bytes_per_frame == 2:
dtype = np.int16
ctype = ctypes.c_int16
elif sample_type == fmt.UnsignedInt and bytes_per_frame == 2:
dtype = np.uint16
ctype = ctypes.c_uint16
#
return dtype, ctype, num_bytes, num_frames, bytes_per_frame, sample_rate
def on_audio_probed(audio_buffer):
"""
"""
cdata = audio_buffer.constData()
(dtype, ctype, num_bytes, num_frames,
bytes_per_frame, sample_rate) = get_buffer_info(audio_buffer)
pointer_addr_str = str(cdata).split("Address ")[1].split(", Size")[0]
pointer_addr = int(pointer_addr_str, 16)
arr = np.array((ctype * num_frames).from_address(pointer_addr))
print(np.linalg.norm(arr)) # should increase in presence of sound
I just tested it with a QAudioRecorder using 16-bit unsigned wavs, and it worked "fine" (audio looked and sounded good, see screenshot below). Again, this is basically a meme code so anything above showing your fancy audio buffered app to your cousins will be extremely risky, do not use in serious code. But in any case let me know if any other workarounds worked for you, or if this also worked in a different context! Hopefully if the devs see that people are actually using this approach they'll fix the issue much sooner :)
Cheers!
Andres
Related
I'm trying to create a Python program that takes input from an Arduino to change the monitor's input port. Currently everything works fine, but when the program runs, the cursor on Windows shows a spinning circle constantly. It's super annoying, and I found out that it is related to the fragment of code below.
# Check serial monitor to see if button has been pressed
while True:
try:
data = arduino.readline()[:-2]
if data:
... Some irrelevant code ...
# Check if input has changed unexpectedly
elif (subprocess.call(r'ControlMyMonitor.exe /GetValue "\\.\DISPLAY1\Monitor0" 60', startupinfo=si) == 15 and current == 17) or (subprocess.Popen(r'ControlMyMonitor.exe /GetValue "\\.\DISPLAY1\Monitor0" 60', startupinfo=si) == 17 and current == 15):
print("Detected monitor switch")
if current == HDMI:
arduino.write('1'.encode())
current = DP
elif current == DP:
arduino.write('2'.encode())
current = HDMI
except:
sys.exit("Communication to Arduino was interrupted")
I'm calling the program ControlMyMonitor.exe multiple times every second to validate the input port value, which is causing the cursor to spin forever when the program is running. Is there a way to prevent this, or should I give up on checking if the monitor input has changed unexpectedly (i.e. no device connected to HDMI port after switching manually, and monitor automatically switches back to DP)?
I ended up solving this by cutting out the middleman. I added a file called moncontrol.py which is pasted below the original fragment of code with my changes. I added the function "get_monitor_input" which returns an integer value of the monitor selected. This way, the code can stay relatively the same.
from datetime import datetime
from ctypes import windll, WinError
import time, os, sys, serial, moncontrol
while True:
handles = []
for handle in moncontrol.iter_physical_monitors(False):
handles.append(handle)
try:
data = arduino.readline()[:-2]
currMon = moncontrol.get_monitor_input(handles[monitor], 0x60)
if data:
... irrelevant code ...
# Check if input has changed unexpectedly
elif (currMon == DP and current == HDMI) or (currMon == HDMI and current == DP):
if current == HDMI:
arduino.write('1'.encode())
current = DP
elif current == DP:
arduino.write('2'.encode())
current = HDMI
else:
destroyHandles(handles)
except serial.SerialException:
destroyHandles(handles)
createLog()
destroyHandles(handles)
def destroyHandles(handles):
for handle in handles:
windll.dxva2.DestroyPhysicalMonitor(handle)
moncontrol.py:
from ctypes import windll, byref, Structure, WinError, POINTER, WINFUNCTYPE
from ctypes.wintypes import BOOL, HMONITOR, HDC, RECT, LPARAM, DWORD, BYTE, WCHAR, HANDLE
_MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM)
class _PHYSICAL_MONITOR(Structure):
_fields_ = [('handle', HANDLE),
('description', WCHAR * 128)]
def iter_physical_monitors(close_handles=True):
"""Iterates physical monitors.
The handles are closed automatically whenever the iterator is advanced.
This means that the iterator should always be fully exhausted!
If you want to keep handles e.g. because you need to store all of them and
use them later, set `close_handles` to False and close them manually."""
def callback(hmonitor, hdc, lprect, lparam):
monitors.append(HMONITOR(hmonitor))
return True
monitors = []
if not windll.user32.EnumDisplayMonitors(None, None, _MONITORENUMPROC(callback), None):
raise WinError('EnumDisplayMonitors failed')
for monitor in monitors:
# Get physical monitor count
count = DWORD()
windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count))
# Get physical monitor handles
physical_array = (_PHYSICAL_MONITOR * count.value)()
windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array)
for physical in physical_array:
yield physical.handle
if close_handles:
if not windll.dxva2.DestroyPhysicalMonitor(physical.handle):
raise WinError()
def get_monitor_input(monitor, code):
current = DWORD()
windll.dxva2.GetVCPFeatureAndVCPFeatureReply(monitor, code, None, byref(current), None)
return current.value
def set_vcp_feature(monitor, code, value):
"""Sends a DDC command to the specified monitor.
See this link for a list of commands:
ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/mccsV3.pdf
"""
if not windll.dxva2.SetVCPFeature(HANDLE(monitor), BYTE(code), DWORD(value)):
raise WinError()
I'm using multiprocess Python module to parallelise processing with OpenCV algorithms (e.g. ORB detector/descriptor). The multiprocess module works fine in most cases, but when it comes to returning a list of cv2.KeyPoint objects there is a problem - all fields of each key point are set to 0 when returned to the caller process, although inside the worker process all key points are correct (as returned by OpenCV).
Here is minimal example that can be used to reproduce the error (you will need an image file called lena.png to make it work):
import numpy as np
from cv2 import ORB_create, imread, cvtColor, COLOR_BGR2GRAY
from multiprocess import Pool
feature = ORB_create(nfeatures=4)
def proc(img):
return feature.detect(img)
def good(feat, frames):
return map(proc, frames)
def bad(feat, frames):
# this starts a worker process
# and then collects result
# but something is lost on the way
pool = Pool(4)
return pool.map(proc, frames)
if __name__ == '__main__':
# it doesn't matter how many images
# a list of images is required to make use of
# pool from multiprocess module
rgb_images = map(lambda fn: imread(fn), ['lena.png'])
grey_images = map(lambda img: cvtColor(img, COLOR_BGR2GRAY), rgb_images)
good_kp = good(feature, grey_images)
bad_kp = bad(feature, grey_images)
# this will fail because elements in
# bad_kp will all contain zeros
for i in range(len(grey_images)):
for x, y in zip(good_kp[i], bad_kp[i]):
# these should be the same
print('good: pt=%s angle=%s size=%s - bad: pt=%s angle=%s size=%s' % (x.pt, x.angle, x.size, y.pt, y.angle, y.size))
assert x.pt == y.pt
Platforms: both CentOS 7.6 and Windows 10 x64
Versions:
Python version: 2.7.15
multiprocess: 0.70.6.1
opencv-python-headless: 3.4.5.20 and 4.0.0.21
Is there a way to work around this? Use of standard multiprocessing module is not an option because of heavy usage of lambdas and callable objects which "can't be pickled".
After some analysis it turned out that the problem is caused by something about cv2.KeyPoint class. This is suggested in a related question and corresponding answer. The problems is that pickle which is apparently used by dill is unable to work with this class.
A simple solution is to avoid sending instances of cv2.KeyPoint between worker and main process. If this is not convenient, then one should wrap data of each keypoint in a simple Python structure or dictionary and pass it.
An example of wrapper could be:
import cv2
class KeyPoint(object):
def __init__(self, kp):
# type: (cv2.KeyPoint) -> None
x, y = kp.pt
self.pt = float(x), float(y)
self.angle = float(kp.angle) if kp.angle is not None else None
self.size = float(kp.size) if kp.size is not None else None
self.response = float(kp.response) if kp.response is not None else None
self.class_id = int(kp.class_id) if kp.class_id is not None else None
self.octave = int(kp.octave) if kp.octave is not None else None
def to_opencv(self):
# type: () -> cv2.KeyPoint
kp = cv2.KeyPoint()
kp.pt = self.pt
kp.angle = self.angle
kp.size = self.size
kp.response = self.response
kp.octave = self.octave
kp.class_id = self.class_id
return kp
I want to build an ecg. the filter is built in udoo, then I want to plot the signal in python. however it keeps getting this while I run my code:
ValueError: could not convert string to float.
import serial
import sys
import time
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
# constants
BAUDE_RATE = 9600
ARDUINO_MAX_INT = 2 ** 10
ARDUINO_MAX_VOLTAGE = 3.3
WINDOW_SIZE = 30
MAX_DATA_SIZE = 1024
# declare the Window
app = QtGui.QApplication([])
win = pg.GraphicsWindow(title="Arduino Analog Plotter")
win.resize(1000, 600)
# initialize plots
raw_plot = win.addPlot(title="Raw Pin Data")
raw_curve = raw_plot.plot(pen='y')
raw_plot.addLegend()
raw_plot.showGrid(True, True)
raw_plot.setYRange(0, 1200)
raw_plot.setXRange(0, 1024)
# disable auto size of the x-y axis
raw_plot.enableAutoRange('xy', False)
raw_data = np.zeros(1024)
# open serial
ser = serial.Serial('COM10', 115200, timeout=1)
line = pg.InfiniteLine(pos=1024, angle=0, pen=(24, 215, 248))
raw_plot.addItem(line)
ser.flushInput()
def gettemp(ser):
ser.write('t')
ser.flush()
return ser.readline().strip('\r').strip('\n').split(' ').pop(7)
def update():
global raw_data
# open serial port
raw_capture = []
for x in range(WINDOW_SIZE):
sensoroutput=gettemp()
r=sensoroutput
ser.readline().strip('\r').strip('\n').split(' ').pop(7)
raw_capture.append(float(r).pop(7))
raw_data = np.concatenate([raw_data, raw_capture])
# remove first bin to make room for new bin
if len(raw_data) > MAX_DATA_SIZE:
raw_data = raw_data[WINDOW_SIZE:]
# plot data
raw_curve.setData(raw_data)
def savecounter():
ser.close()
import atexit
atexit.register(savecounter)
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Does anybody know how to fix this?
Your sensoroutput seems to be a string.
You cannot convert it directly with float() :
raw_capture.append(float(r).pop(7))
Can you post what is the output of sensoroutput?
I'm just taking a wild stab here, but usually if you have both \r and \n for line endings the \r comes first and the \n comes second. The way you're stripping off those characters, the \r will remain because you try to strip it first, before the \n has been removed; float() will fail on the non-numeric character in the string. Try this instead, it will remove both end-of-line characters at the same time:
ser.readline().strip('\r\n').split(' ').pop(7)
THIS link describes a way to set a QPixmap without using QImage.
How can I reproduce this in python?
what I have is a buffer, which represents an image.
what I need is to call something like
label.setPixmap(QPixmap.loadFromData(buffer))
to display the image.
But before that, a format header has to be inserted, according to the link for a 300x300 grayscale image the header is something like "P6 300 300 255". I have no idea how to do that.
Here is the whole script:
import numpy as np
import cv2
import sys
from PySide.QtGui import QApplication,QLabel,QPixmap
if QApplication.instance() is not None:
a=QApplication.instance()
else:
a = QApplication(sys.argv)
lbl=QLabel()
header = bytearray(b"P6 20 20 255")
cvImg=np.zeros((20,20),dtype=np.uint8)
cv2.circle(cvImg,(9,9),9,49,-1)
buff=cvImg.data
ppm=header+buff
pixmap=QPixmap()
lbl.setPixmap(pixmap.loadFromData(ppm,40,format="PPM"))
sys.exit(a.exec_())
It says:
TypeError: 'PySide.QtGui.QPixmap.loadFromData' called with wrong argument types:
PySide.QtGui.QPixmap.loadFromData(bytearray, int, str)
Supported signatures:
PySide.QtGui.QPixmap.loadFromData(PySide.QtCore.QByteArray, str = None, PySide.QtCore.Qt.ImageConversionFlags = Qt.AutoColor)
PySide.QtGui.QPixmap.loadFromData(PySide.QtCore.uchar, unsigned int, str = None, PySide.QtCore.Qt.ImageConversionFlags = Qt.AutoColor)
I am attempting to create a program in python that plays a particular harpsichord note when a certain key is pressed. I want it to remain responsive so you can continue to play more notes (kind of like a normal electric piano.) However, because the wav files that the notes are stored in are about 7-10 seconds long I am experiencing some issues. I can press at least 10 keys per second. So, over the duration of one note I could have around 100 different wav files playing at once. I tried to use winsound, but it was unable to play multiple wav files at once. I then moved on to PyAudio and it works kind of. The only way that I found to accomplish what I wanted was this:
from msvcrt import getch
import pyaudio
import wave
import multiprocessing as mp
#This function is just code for playing a sound in PyAudio
def playNote(filename):
CHUNK = 1024
wf = wave.open(filename, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
data = wf.readframes(CHUNK)
while data != '':
stream.write(data)
data = wf.readframes(CHUNK)
stream.stop_stream()
stream.close()
p.terminate()
if __name__ == "__main__":
while True:
#If the 'a' key is pressed: start a new process that calls playNote
#and pass in the file name for a note.
if ord(getch()) == 97: #a
mp.Process(target=playNote, args=("F:\Project Harpsichord\The wavs\A1.wav",)).start()
#If the 's' key is pressed: start a new process that calls playNote
#and pass in the file name for another note.
if ord(getch()) == 115: #s
mp.Process(target=playNote, args=("F:\Project Harpsichord\The wavs\A0.wav",)).start()
Basically whenever I want to play a new wav, I have to start a new process that runs the code in the playNote function. As I already stated I can potentially have up to 100 of these playing at once. Suffice it to say, one hundred copies of the python interpreter all running at once almost crashed my computer. I also tried a similar approach with multi-threading, but had the same problems.
This post shows a way to mix multiple wav files together so they can be played at the same time, but since my program will not necessarily be starting the sounds at the same time I am unsure if this will work.
I need an efficient way to play multiple notes at the same time. Whether this comes in the form of another library, or even a different language I really don't care.
I checked out pygame like J.F Sebastian suggested. It ended up being exactly what I needed. I used pygame.mixer.Sound() in conjunction with pygame.mixer.set_num_channels(). Here's what I came up with.
import pygame as pg
import time
pg.mixer.init()
pg.init()
a1Note = pg.mixer.Sound("F:\Project Harpsichord\The wavs\A1.wav")
a2Note = pg.mixer.Sound("F:\Project Harpsichord\The wavs\A0.wav")
pg.mixer.set_num_channels(50)
for i in range(25):
a1Note.play()
time.sleep(0.3)
a2Note.play()
time.sleep(0.3)
This doesn't really solve your problem, but it's too long for the comments, and it may be useful. I gave it a bash, got defeated on a few fronts - giving up and going for pizza. Audio is really not my thing, but it was quite a lot of fun playing around with it.
Give Pydub a look. I've Played around with a couple of methods, but haven't had any satisfactory success. This answer here explains quite a few things regarding adding two signals together nicely. I assume that the static you have is because of clipping.
Sorry that I didn't deliver, but I may as well post all the things I've created in case you or someone else wants to grab something from it:
#using python 2.7
#example animal sounds from http://www.wavsource.com/animals/animals.htm
#note that those sounds have lots of different sampling rates and encoding types. Causes problems.
#required installs:
#numpy
#scipy
#matplotlib
#pyaudio -sudo apt-get install python-pyaudio
#pydub: -pip install pydub
def example():
"example sounds and random inputs"
sExampleSoundsDir = "/home/roman/All/Code/sound_files"
sExampleFile1 = 'bird.wav'
sExampleFile2 = 'frog.wav'
oJ = Jurgenmeister(sExampleSoundsDir)
#load audio into numpy array
dSound1 = oJ.audio2array(sExampleFile1)
dSound2 = oJ.audio2array(sExampleFile2)
#Simply adding the arrays is noisy...
dResSound1 = oJ.resample(dSound1)
dResSound2 = oJ.resample(dSound2)
dJoined = oJ.add_sounds(dResSound1, dResSound2)
#pydub method
oJ.overlay_sounds(sExampleFile1, sExampleFile2)
#listen to the audio - mixed success with these sounds.
oJ.play_array(dSound1)
oJ.play_array(dSound2)
oJ.play_array(dResSound1)
oJ.play_array(dResSound2)
oJ.play_array(dJoined)
#see what the waveform looks like
oJ.plot_audio(dJoined)
class Jurgenmeister:
"""
Methods to play as many sounds on command as necessary
Named in honour of op, and its as good a name as I can come up with myself.
"""
def __init__(self, sSoundsDir):
import os
import random
lAllSounds = os.listdir(sSoundsDir)
self.sSoundsDir = sSoundsDir
self.lAllSounds = lAllSounds
self.sRandSoundName = lAllSounds[random.randint(0, len(lAllSounds)-1)]
def play_wave(self, sFileName):
"""PyAudio play a wave file."""
import pyaudio
import wave
iChunk = 1024
sDir = "{}/{}".format(self.sSoundsDir, sFileName)
oWave = wave.open(sDir, 'rb')
oPyaudio = pyaudio.PyAudio()
oStream = oPyaudio.open(
format = oPyaudio.get_format_from_width(oWave.getsampwidth()),
channels = oWave.getnchannels(),
rate = oWave.getframerate(),
output = True
)
sData = oWave.readframes(iChunk)
while sData != '':
oStream.write(sData)
sData = oWave.readframes(iChunk)
oStream.stop_stream()
oStream.close()
oPyaudio.terminate()
def audio2array(self, sFileName):
"""
Returns monotone data for a wav audio file in form:
iSampleRate, aNumpySignalArray, aNumpyTimeArray
Should perhaps do this with scipy again, but I threw that code away because I wanted
to try the pyaudio package because of its streaming functions. They defeated me.
"""
import wave
import numpy as np
sDir = "{}/{}".format(self.sSoundsDir, sFileName)
oWave = wave.open(sDir,"rb")
tParams = oWave.getparams()
iSampleRate = tParams[2] #frames per second
iLen = tParams[3] # number of frames
#depending on the type of encoding of the file. Usually 16
try:
sSound = oWave.readframes(iLen)
oWave.close()
aSound = np.fromstring(sSound, np.int16)
except ValueError:
raise ValueError("""wave package seems to want all wav incodings to be in int16, else it throws a mysterious error.
Short way around it: find audio encoded in the right format. Or use scipy.io.wavfile.
""")
aTime = np.array( [float(i)/iSampleRate for i in range(len(aSound))] )
dRet = {
'iSampleRate': iSampleRate,
'aTime': aTime,
'aSound': aSound,
'tParams': tParams
}
return dRet
def resample(self, dSound, iResampleRate=11025):
"""resample audio arrays
common audio sample rates are 44100, 22050, 11025, 8000
#creates very noisy results sometimes.
"""
from scipy import interpolate
import numpy as np
aSound = np.array(dSound['aSound'])
iOldRate = dSound['iSampleRate']
iOldLen = len(aSound)
rPeriod = float(iOldLen)/iOldRate
iNewLen = int(rPeriod*iResampleRate)
aTime = np.arange(0, rPeriod, 1.0/iOldRate)
aTime = aTime[0:iOldLen]
oInterp = interpolate.interp1d(aTime, aSound)
aResTime = np.arange(0, aTime[-1], 1.0/iResampleRate)
aTime = aTime[0:iNewLen]
aResSound = oInterp(aResTime)
aResSound = np.array(aResSound, np.int16)
tParams = list(x for x in dSound['tParams'])
tParams[2] = iResampleRate
tParams[3] = iNewLen
tParams = tuple(tParams)
dResSound = {
'iSampleRate': iResampleRate,
'aTime': aResTime,
'aSound': aResSound,
'tParams': tParams
}
return dResSound
def add_sounds(self, dSound1, dSound2):
"""join two sounds together and return new array
This method creates a lot of clipping. Not sure how to get around that.
"""
if dSound1['iSampleRate'] != dSound2['iSampleRate']:
raise ValueError('sample rates must be the same. Please resample first.')
import numpy as np
aSound1 = dSound1['aSound']
aSound2 = dSound2['aSound']
if len(aSound1) < len(aSound2):
aRet = aSound2.copy()
aRet[:len(aSound1)] += aSound1
aTime = dSound2['aTime']
tParams = dSound2['tParams']
else:
aRet = aSound1.copy()
aRet[:len(aSound2)] += aSound2
aTime = dSound1['aTime']
tParams = dSound1['tParams']
aRet = np.array(aRet, np.int16)
dRet = {
'iSampleRate': dSound1['iSampleRate'],
'aTime': aTime,
'aSound': aRet,
'tParams': tParams
}
return dRet
def overlay_sounds(self, sFileName1, sFileName2):
"I think this method warrants a bit more exploration
Also very noisy."
from pydub import AudioSegment
sDir1 = "{}/{}".format(self.sSoundsDir, sFileName1)
sDir2 = "{}/{}".format(self.sSoundsDir, sFileName2)
sound1 = AudioSegment.from_wav(sDir1)
sound2 = AudioSegment.from_wav(sDir2)
# mix sound2 with sound1, starting at 0ms into sound1)
output = sound1.overlay(sound2, position=0)
# save the result
sDir = "{}/{}".format(self.sSoundsDir, 'OUTPUT.wav')
output.export(sDir, format="wav")
def array2audio(self, dSound, sDir=None):
"""
writes an .wav audio file to disk from an array
"""
import struct
import wave
if sDir == None:
sDir = "{}/{}".format(self.sSoundsDir, 'OUTPUT.wav')
aSound = dSound['aSound']
tParams = dSound['tParams']
sSound = struct.pack('h'*len(aSound), *aSound)
oWave = wave.open(sDir,"wb")
oWave.setparams(tParams)
oWave.writeframes(sSound)
oWave.close()
def play_array(self, dSound):
"""Tried to use use pyaudio to play array by just streaming it. It didn't behave, and I moved on.
I'm just not getting the pyaudio stream to play without weird distortion
when not loading from file. Perhaps you have more luck.
"""
self.array2audio(dSound)
self.play_wave('OUTPUT.wav')
def plot_audio(self, dSound):
"just plots the audio array. Nice to see plots when things are going wrong."
import matplotlib.pyplot as plt
plt.plot(dSound['aTime'], dSound['aSound'])
plt.show()
if __name__ == "__main__":
example()
I also get this error when I use wave. It still works, so I just ignore it.
Problem seems to be widespread. Error lines:
ALSA lib pcm_dsnoop.c:618:(snd_pcm_dsnoop_open) unable to open slave
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
Good luck!