Last active
October 19, 2016 12:46
-
-
Save pbouda/5531821 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
# PyQt4 imports | |
from PyQt4 import QtGui, QtCore, QtOpenGL | |
from PyQt4.QtOpenGL import QGLWidget | |
# PyOpenGL imports | |
import OpenGL.GL as gl | |
import OpenGL.arrays.vbo as glvbo | |
import types | |
import ctypes | |
import pyaudio | |
import wave | |
# Window creation function. | |
def create_window(window_class): | |
"""Create a QT window in Python, or interactively in IPython with QT GUI | |
event loop integration: | |
# in ~/.ipython/ipython_config.py | |
c.TerminalIPythonApp.gui = 'qt' | |
c.TerminalIPythonApp.pylab = 'qt' | |
See also: | |
http://ipython.org/ipython-doc/dev/interactive/qtconsole.html#qt-and-the-qtconsole | |
""" | |
app_created = False | |
app = QtCore.QCoreApplication.instance() | |
if app is None: | |
app = QtGui.QApplication(sys.argv) | |
app_created = True | |
app.references = set() | |
window = window_class() | |
app.references.add(window) | |
window.show() | |
if app_created: | |
app.exec_() | |
return window | |
def compile_vertex_shader(source): | |
"""Compile a vertex shader from source.""" | |
vertex_shader = gl.glCreateShader(gl.GL_VERTEX_SHADER) | |
gl.glShaderSource(vertex_shader, source) | |
gl.glCompileShader(vertex_shader) | |
# check compilation error | |
result = gl.glGetShaderiv(vertex_shader, gl.GL_COMPILE_STATUS) | |
if not(result): | |
raise RuntimeError(gl.glGetShaderInfoLog(vertex_shader)) | |
return vertex_shader | |
def compile_fragment_shader(source): | |
"""Compile a fragment shader from source.""" | |
fragment_shader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) | |
gl.glShaderSource(fragment_shader, source) | |
gl.glCompileShader(fragment_shader) | |
# check compilation error | |
result = gl.glGetShaderiv(fragment_shader, gl.GL_COMPILE_STATUS) | |
if not(result): | |
raise RuntimeError(gl.glGetShaderInfoLog(fragment_shader)) | |
return fragment_shader | |
def link_shader_program(vertex_shader, fragment_shader=None): | |
"""Create a shader program with from compiled shaders.""" | |
program = gl.glCreateProgram() | |
gl.glAttachShader(program, vertex_shader) | |
if fragment_shader is not None: | |
gl.glAttachShader(program, fragment_shader) | |
gl.glLinkProgram(program) | |
# check linking error | |
result = gl.glGetProgramiv(program, gl.GL_LINK_STATUS) | |
if not(result): | |
raise RuntimeError(gl.glGetProgramInfoLog(program)) | |
return program | |
VS = """ | |
#version 330 | |
in vec4 position; | |
void main() | |
{ | |
gl_Position = position; | |
} | |
""" | |
FS = """#version 330 | |
uniform vec2 resolution; | |
uniform vec2 spec; | |
uniform float time; | |
void main(void) { | |
vec2 uv = 2.0 * (gl_FragCoord.xy / resolution) - 1.0; | |
float col = 0.0; | |
uv.x += sin(time*6.0 + uv.y*1.5) * spec.y; | |
col += abs(0.066/uv.x) * spec.y; | |
gl_FragColor = vec4(col,col,col,1.0); | |
} | |
""" | |
chunk = 2048 | |
FORMAT = pyaudio.paInt16 | |
CHANNELS = 1 | |
RATE = 44100 | |
RECORD_SECONDS = 20 | |
class GLPlotWidget(QGLWidget): | |
# default window size | |
width, height = 1280, 800 | |
timer_span = 40 | |
def initializeGL(self): | |
"""Initialize OpenGL, VBOs, upload data on the GPU, etc.""" | |
# background color | |
print gl.glGetString(gl.GL_SHADING_LANGUAGE_VERSION) | |
print gl.glGetString(gl.GL_VERSION) | |
gl.glClearColor(0, 0, 0, 0) | |
# create a Vertex Buffer Object with the specified data | |
self.update_vbo() | |
self.vs = VS | |
self.fs = FS | |
self.stream = None | |
self.link_shaders() | |
self.timer = QtCore.QTimer(self) | |
self.timer.timeout.connect(self.updateWorld) | |
self.timer.start(self.timer_span) | |
self.running_time = 0 | |
self.my_init() | |
def my_init(self): | |
self.get_uniforms() | |
p = pyaudio.PyAudio() | |
if self.stream and self.stream.is_active(): | |
self.stream.close() | |
self.stream = p.open(format = FORMAT, | |
channels = CHANNELS, | |
rate = RATE, | |
input = True, | |
frames_per_buffer = chunk) | |
self.window = np.blackman(chunk) | |
def get_uniforms(self): | |
self.resolution = gl.glGetUniformLocation(self.shaders_program, "resolution") | |
gl.glUniform2f(self.resolution, self.width, self.height) | |
#print self.resolution | |
self.spec = gl.glGetUniformLocation(self.shaders_program, "spec") | |
gl.glUniform2f(self.spec, 0.2, 0.1) | |
#print self.spec | |
self.time = gl.glGetUniformLocation(self.shaders_program, "time") | |
def link_shaders(self): | |
# compile the vertex shader | |
vs = compile_vertex_shader(self.vs) | |
# compile the fragment shader | |
fs = compile_fragment_shader(self.fs) | |
self.shaders_program = link_shader_program(vs, fs) | |
gl.glUseProgram(self.shaders_program) | |
def update_vbo(self): | |
self.vbo = glvbo.VBO(self.data) | |
def updateWorld(self): | |
self.running_time += self.timer_span | |
self.move_stuff() | |
self.updateGL() | |
def move_stuff(self): | |
a = self.stream.read(chunk) | |
indata = np.array(wave.struct.unpack("%dh"%(chunk), a))*self.window | |
fft_data = abs(np.fft.rfft(indata)) | |
total_sum = np.sum(fft_data) | |
spec_x = np.sum(fft_data[40:100])/(len(fft_data[40:100])*chunk*2048) | |
spec_y = np.sum(fft_data[250:])/(len(fft_data[250:])*chunk*32) | |
gl.glUniform2f(self.spec, spec_x, spec_y) | |
def paintGL(self): | |
"""Paint the scene.""" | |
gl.glClearColor(0.0, 0.0, 0.0, 0.0) | |
gl.glClear(gl.GL_COLOR_BUFFER_BIT) | |
gl.glUniform1f(self.time, self.running_time / 1000.0) | |
self.vbo.bind() | |
gl.glEnableVertexAttribArray(0) | |
#gl.glEnableVertexAttribArray(1) | |
gl.glVertexAttribPointer(0, 4, gl.GL_FLOAT, gl.GL_FALSE, 0, None) | |
#gl.glVertexAttribPointer(1, 4, gl.GL_FLOAT, gl.GL_FALSE, 0, ctypes.c_void_p(48)) | |
gl.glDrawArrays(gl.GL_QUADS, 0, 4) | |
def resizeGL(self, width, height): | |
"""Called upon window resizing: reinitialize the viewport.""" | |
# update the window size | |
self.width, self.height = width, height | |
# paint within the whole window | |
gl.glViewport(0, 0, width, height) | |
gl.glUniform2f(self.resolution, width, height) | |
import numpy as np | |
vertex_data_rect = [ -1.0, -1.0, 0.0, 1.0, | |
1.0, -1.0, 0.0, 1.0, | |
1.0, 1.0, 0.0, 1.0, | |
-1.0, 1.0, 0.0, 1.0 ] | |
data = np.array(vertex_data_rect, np.float32) | |
# define a QT window with an OpenGL widget inside it | |
class TestWindow(QtGui.QMainWindow): | |
def __init__(self): | |
super(TestWindow, self).__init__() | |
# initialize the GL widget | |
self.widget = GLPlotWidget() | |
self.widget.data = data | |
# put the window at the screen position (100, 100) | |
#self.setGeometry(100, 100, self.widget.width, self.widget.height) | |
self.setCentralWidget(self.widget) | |
self.showFullScreen() |
Years later have tried this again and run it successfully :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi,
I've been playing with this trying to get it to work ... the GL Window is completely just black -
This is the output I get at the end:
I guess 'time' just needs to be added somewhere with. Is 3.0 the shader level ? - I'm not super familar with PyQt, but I guess there is a way to specify an opengl 3.3 context (I should have 3.3 available).