As it happens I have a bit of experience with something like that, although
I tend to use the OpenAL bindings in Pyglet over the convience functions
like pyglet.media. I've modified one of my examples (you'll find it
attached) from Peter Meijers <http://www.seeingwithsound.com/im2sound.htm>
image to sound rendering to just output a numpy waveform array.
Alternatively you could also have a look at pylet.media.Procedural for
generating sound, although that may not meet your numpy requirements.
--
You received this message because you are subscribed to the Google Groups
"pyglet-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/pyglet-users.
For more options, visit https://groups.google.com/d/optout.
import math
import struct
import ctypes
import numpy as np
#using pyglet
import pyglet
from pyglet.media.drivers.openal import lib_openal as al
from pyglet.media.drivers.openal import lib_alc as alc
#using PyAL
##from openal import al, alc
class Example(object):
def __init__(self):
self.listener = Listener()
self.sbuffer = buffer_sound()
self.generator = generator()
self.player = Player()
self.sbuffer.load(self.generator.output)
self.player.add(self.sbuffer)
self.player.play()
while self.player.playing():
pass
self.player.remove()
self.player.delete()
self.sbuffer.delete()
self.listener.delete()
class generator(object):
#pre-calculate waveform values
def __init__(self):
self.speed = 1.0
self.byte = int(20000 * self.speed)
self.size = 64
self.ns = self.byte
self.TwoPi = 6.283185307179586476925287 #double Pi
self.rnd = 0.211321159122
self.phi0 = self.TwoPi * self.rnd
#Generate sine wave
self.w = np.asarray(range(self.size))
self.w = self.TwoPi * 500 * np.power(1.0*5000/500, 1.0*self.w/(self.size-1))
self.w = np.tile(self.w,(self.ns,1))
self.core = np.asarray(range(self.ns)) #312.5 * data(4096), repeat
self.core = np.reshape(self.core,(self.byte,1))
self.core = np.repeat(self.core,(self.size),axis=1)
self.w = np.sin(self.w * (self.core * (1.0 / self.byte)) + self.phi0)
#create ctypes array
self.output = (ctypes.c_short * self.ns)()
#flatten wave array
self.w = np.ravel(self.w)
#multiply wave to make it audiable
self.w = self.w * 32767.0
#load wave into ctypes array to into OpenAL buffer
for a in xrange(0,len(self.output),1):
self.output[a] = self.output[a]+int(self.w[a])
class buffer_sound(object):
def __init__(self):
self.channels = 1
self.bitrate = 16
self.samplerate = 21000
self.wavbuf = None
self.alformat = al.AL_FORMAT_MONO16
self.length = None
## formatmap = {
## (1, 8) : al.AL_FORMAT_MONO8,
## (2, 8) : al.AL_FORMAT_STEREO8,
## (1, 16): al.AL_FORMAT_MONO16,
## (2, 16) : al.AL_FORMAT_STEREO16,
## }
## alformat = formatmap[(channels, bitrate)]
self.buf = al.ALuint(0)
al.alGenBuffers(1, self.buf)
def load(self,data):
self.wavbuf = data
self.length = len(data)
#allocate buffer space to: buffer, format, data, len(data), and samplerate
al.alBufferData(self.buf, self.alformat, self.wavbuf, len(self.wavbuf)*2, self.samplerate)
def delete(self):
al.alDeleteBuffers(1, self.buf)
#load a listener to load and play sounds.
class Listener(object):
def __init__(self):
#load device/context/listener
self.device = alc.alcOpenDevice(None)
self.context = alc.alcCreateContext(self.device, None)
alc.alcMakeContextCurrent(self.context)
#set player position
def _set_position(self,pos):
self._position = pos
x,y,z = map(int, pos)
al.alListener3f(al.AL_POSITION, x, y, z)
def _get_position(self):
return self._position
#delete current listener
def delete(self):
alc.alcDestroyContext(self.context)
alc.alcCloseDevice(self.device)
position = property(_get_position, _set_position,doc="""get/set position""")
#load sound buffers into an openal source player to play them
class Player(object):
#load default settings
def __init__(self):
#load source player
self.source = al.ALuint(0)
al.alGenSources(1, self.source)
#disable rolloff factor by default
al.alSourcef(self.source, al.AL_ROLLOFF_FACTOR, 0)
#disable source relative by default
al.alSourcei(self.source, al.AL_SOURCE_RELATIVE,0)
#capture player state buffer
self.state = al.ALint(0)
#set internal variable tracking
self._volume = 1.0
self._pitch = 1.0
self._position = [0,0,0]
self._rolloff = 1.0
self._loop = False
self.queue = []
#set rolloff factor, determines volume based on distance from listener
def _set_rolloff(self,value):
self._rolloff = value
al.alSourcef(self.source, al.AL_ROLLOFF_FACTOR, value)
def _get_rolloff(self):
return self._rolloff
#set whether looping or not - true/false 1/0
def _set_loop(self,lo):
self._loop = lo
al.alSourcei(self.source, al.AL_LOOPING, lo)
def _get_loop(self):
return self._loop
#set player position
def _set_position(self,pos):
self._position = pos
x,y,z = map(int, pos)
al.alSource3f(self.source, al.AL_POSITION, x, y, z)
def _get_position(self):
return self._position
#set pitch - 1.5-0.5 float range only
def _set_pitch(self,pit):
self._pitch = pit
al.alSourcef(self.source, al.AL_PITCH, pit)
def _get_pitch(self):
return self._pitch
#set volume - 1.0 float range only
def _set_volume(self,vol):
self._volume = vol
al.alSourcef(self.source, al.AL_GAIN, vol)
def _get_volume(self):
return self._volume
#queue a sound buffer
def add(self,sound):
al.alSourceQueueBuffers(self.source, 1, sound.buf) #self.buf
self.queue.append(sound)
#remove a sound from the queue (detach & unqueue to properly remove)
def remove(self):
if len(self.queue) > 0:
al.alSourceUnqueueBuffers(self.source, 1, self.queue[0].buf) #self.buf
al.alSourcei(self.source, al.AL_BUFFER, 0)
self.queue.pop(0)
#play sound source
def play(self):
al.alSourcePlay(self.source)
#get current playing state
def playing(self):
al.alGetSourcei(self.source, al.AL_SOURCE_STATE, self.state)
if self.state.value == al.AL_PLAYING:
return True
else:
return False
#stop playing sound
def stop(self):
al.alSourceStop(self.source)
#rewind player
def rewind(self):
al.alSourceRewind(self.source)
#pause player
def pause(self):
al.alSourcePause(self.source)
#delete sound source
def delete(self):
al.alDeleteSources(1, self.source)
#Go straight to a set point in the sound file
def _set_seek(self,offset):#float 0.0-1.0
al.alSourcei(self.source,al.AL_BYTE_OFFSET,int(self.queue[0].length * offset))
#returns current buffer length position (IE: 21000), so divide by the buffers self.length
def _get_seek(self):#returns float 0.0-1.0
al.alGetSourcei(self.source, al.AL_BYTE_OFFSET, self.state)
return float(self.state.value)/float(self.queue[0].length)
rolloff = property(_get_rolloff, _set_rolloff,doc="""get/set rolloff factor""")
volume = property(_get_volume, _set_volume,doc="""get/set volume""")
pitch = property(_get_pitch, _set_pitch, doc="""get/set pitch""")
loop = property(_get_loop, _set_loop, doc="""get/set loop state""")
position = property(_get_position, _set_position,doc="""get/set position""")
seek = property(_get_seek, _set_seek, doc="""get/set the current play position""")
Example()