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 pyglet-users+unsubscr...@googlegroups.com. To post to this group, send email to pyglet-users@googlegroups.com. 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()