Play multiple sounds at once

I am trying to create a python program that plays a specific note of a harpsichord when a certain key is pressed. I want it to stay responsive so that you can continue to play more notes (like a regular electric piano). However, since the wav files in which notes are stored lasts about 7-10 seconds, I am having some problems. I can press at least 10 keys per second. Thus, in one note, I could use about 100 different wav files at the same time. I tried to use winsound, but it could not play several wav files at once. Then I switched to PyAudio and it works. The only way I found to achieve 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 need to start a new process that runs the code in the playNote function. As I said, I can have up to 100 of these games at once. Suffice it to say that a hundred instances of the python interpreter that all worked right away almost crashed my computer. I also tried a similar approach with multi-threaded, but had the same problems.

This post shows a way to combine several wav files together so that they can play at the same time, but since my program will not necessarily start sounds at the same time, I'm not sure if this will work. I need an efficient way to play multiple notes at once. Whether it's in the form of another library or even another language, I really don't care.

+6
source share
2 answers

I checked pygame as suggested by JF Sebastian. That turned out to be exactly what I needed. I used pygame.mixer.Sound () in combination with pygame.mixer. set_num_channels () . Here is 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) 
+2
source

It really does not solve your problem, but is too long for comments, and it can be useful. I gave him bash, was defeated on several fronts - refused and went for pizza. The audio is really not mine, but it was pretty fun playing with it.

Give Pydub . I played with several methods, but did not have satisfactory success. This answer explains a lot of things related to the good addition of two signals. I assume that you have a static one due to clipping.

Sorry I didn’t deliver, but I can also publish everything that I created if you or someone else wants to extract 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 a wave. It still works, so I just ignore it. The problem appears 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

+2
source

All Articles