pygameでsound shaderを使い音を鳴らす
普段使っているsound shaderをpygameに組み込んでみた。
transform feedbackを使っているのでグラフィック部分は要らないのでOpenGLのcontextは短く書ける。
compute shaderでやる方法もあるので、そっちも試したい。
とりあえず、コードだけ。
from os import environ environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1' import pygame from OpenGL.GL import * from OpenGL.WGL import * from ctypes import * import pyaudio import numpy user32 = windll.user32 width,height = 340,240 pygame.init() screen_display = pygame.display display = screen_display.set_mode((width,height)) # Get OpenGL context hWnd = user32.CreateWindowExA(0,0xC018,0,0,0,0,0,0,0,0,0,0) hDC = user32.GetDC(hWnd) pfd = PIXELFORMATDESCRIPTOR(0,1,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0) SetPixelFormat(hDC,ChoosePixelFormat(hDC, pfd), pfd) hGLrc = wglCreateContext(hDC) wglMakeCurrent(hDC, hGLrc) # audio chunk = 4096 rate = 48000 channels = 2 samples = (c_float * chunk * channels)() chunkData=numpy.frombuffer(samples, dtype=numpy.float32) vbo = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, vbo) glBufferData(GL_ARRAY_BUFFER, sizeof(samples), None, GL_STATIC_DRAW) glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo) program = glCreateProgram() shader = glCreateShader(GL_VERTEX_SHADER) glsl=''' #version 300 es precision highp float; out vec2 gain; uniform float iFrameCount; const float iChunk = {0:.1f}; const float iSampleRate = {1:.1f}; '''.format(chunk ,rate)+''' vec2 mainSound( int samp, float time ); void main(){ float time = (iChunk * iFrameCount + float(gl_VertexID)) / iSampleRate; gain = clamp(mainSound(int(iSampleRate),time), -1.0, 1.0); } ''' glsl += ''' vec2 mainSound(int samp,float time){ return 0.5*vec2(sin(3.14*440.*time)); } ''' glShaderSource(shader, glsl) glCompileShader(shader) if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE: raise RuntimeError(glGetShaderInfoLog(shader).decode()) glAttachShader(program, shader) glDeleteShader(shader) outs = cast((c_char_p*1)(b"gain"), POINTER(POINTER(c_char))) glTransformFeedbackVaryings(program, 1, outs, GL_INTERLEAVED_ATTRIBS) glLinkProgram(program) glUseProgram(program) p = pyaudio.PyAudio() stream = p.open( format = pyaudio.paFloat32, channels = channels, rate = rate, frames_per_buffer = chunk, output=True, input=False ) stream.start_stream() tick = 0 running = True while running: glUniform1f(glGetUniformLocation(program, "iFrameCount"), tick) glEnable(GL_RASTERIZER_DISCARD) glBeginTransformFeedback(GL_POINTS) glDrawArrays(GL_POINTS, 0, chunk) glEndTransformFeedback() glDisable(GL_RASTERIZER_DISCARD) glGetBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(samples), byref(samples)) data=numpy.frombuffer(samples, dtype=numpy.float32) stream.write(data.tobytes()) tick += 1 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False stream.stop_stream() stream.close() # OpenGL finsh wglMakeCurrent(0, 0) wglDeleteContext(hGLrc) user32.ReleaseDC(hWnd, hDC) user32.PostQuitMessage(0) user32.DestroyWindow(hWnd) pygame.quit()