0

I'm using scapy and pyaudio to play a short length of sine wave every time a packet is sent or received, with the frequency of the sound indicating the sender address.

sniff(prn = makeSound)

In this code, the makeSound function takes a packet, dissects it, and plays a sound based on the sender address.

As far as I know, pyaudio has either blocking or callback mode. In neither of these modes can I play multiple sounds simultaneously.

I need a way to start a note, and have it immediately start being mixed into the audio stream, regardless of how many sine waves are already in the process of being played.

is8ac
  • 13
  • 3
  • Can't you fire them off in different threads? – Rolf of Saxony Aug 22 '15 at 15:48
  • Manual threading seems like overkill for something like this. Is there some sample code showing how I would do this? – is8ac Aug 22 '15 at 16:26
  • If threading seems like too much work, then how about Timer() – Rolf of Saxony Aug 22 '15 at 16:47
  • Oops! That is wxpython specific I think! – Rolf of Saxony Aug 22 '15 at 17:11
  • What I eventually did was use `os.system('play --no-show-progress --null --channels 1 synth %s sine %f &' % ( 0.1, srcHz))`. The '&' at the end allows the shell to be non blocking. This is of course terribly kludgy, but it works. I will probably upgrade it to in-python threading as per Rolf of Saxony's suggestion. – is8ac Aug 23 '15 at 15:22
  • Ha! I tested using sox to blast them onto the command line but decided that you wouldn't accept that as an answer, as whilst being practical it's not very pythonic! :) – Rolf of Saxony Aug 23 '15 at 15:30

1 Answers1

0

You need to perform the mixing yourself. See the following example-program on how to mix several voices into one single-channel output.

 import struct
 import math
 import pyaudio
 from itertools import count

 pa = pyaudio.PyAudio()

 FORMAT = pyaudio.paFloat32
 CHANNELS = 1
 RATE = 44100

 OUTPUT_BLOCK_TIME = 0.05
 OUTPUT_FRAMES_PER_BLOCK = int(RATE*OUTPUT_BLOCK_TIME)


 def sine_gen():
     time = 0
     format = "%df" % OUTPUT_FRAMES_PER_BLOCK
     voices = []
     voices.append(lambda sampletime: math.sin(sampletime * math.pi * 2 * 440.0))

     for frame in count():
         block = []
         for i in xrange(OUTPUT_FRAMES_PER_BLOCK):
             sampletime = time + (float(i) / OUTPUT_FRAMES_PER_BLOCK) * OUTPUT_BLOCK_TIME
             sample = sum(voice(sampletime) for voice in voices) / len(voices)
             block.append(sample)
         yield struct.pack(format, *block)
         time += OUTPUT_BLOCK_TIME
         if frame == 20:
             voices.append(
                 lambda sampletime: math.sin(sampletime * math.pi * 2 * 880.0)
             )

 stream = pa.open(format=FORMAT,
     channels=CHANNELS, rate=RATE, output=1)

 for i, block in enumerate(sine_gen()):
     stream.write(block)
deets
  • 6,285
  • 29
  • 28