テラByteの時代にキロByte

shader又はdemosceneに関係する事

SSBOのデータを抜き取る (compute shader)

音楽を作る為にSSBOのデータを抜き取ってみます。

samples = (c_float * SND_NUMSAMPLESC)()
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(samples), byref(samples))    

この部分です。今回は音楽ですので簡単ですが、numpyとかで抜き取ってデータ加工とかにも流用できるのでは、ないのでしょうか。

from OpenGL.GL import *
from OpenGL.WGL import *
from ctypes import *
from ctypes.wintypes import *
import sys
import math

csh = """
#version 430

layout(binding=7) buffer Buf{ vec2 p[];};
layout(local_size_x = 128, local_size_y = 1, local_size_z = 1) in;

#define sampleRate 44100

vec2 mainSound(float time)
{
  return vec2(sin(6.2831*440.0*fract(time)));
}

void main(){
  uint id = gl_GlobalInvocationID.x;
  float time = float(id) / sampleRate;
  p[id] =  mainSound(time);
}
"""

winmm = windll.winmm 
kernel32 = windll.kernel32
user32 = windll.user32

WS_OVERLAPPEDWINDOW = 0xcf0000
WS_VISIBLE = 0x10000000
PM_REMOVE = 1
WM_NCLBUTTONDOWN = 161
HTCLOSE = 20
VK_ESCAPE = 27
PFD_SUPPORT_OPENGL = 32
PFD_DOUBLEBUFFER = 1
WAVE_FORMAT_IEEE_FLOAT = 3
WAVE_MAPPER = -1 
TIME_SAMPLES = 2

class WAVEFORMATEX(Structure): 
    _fields_ = [ 
            ("wFormatTag", WORD), 
            ("nChannels", WORD), 
            ("nSamplesPerSec", DWORD), 
            ("nAvgBytesPerSec", DWORD), 
            ("nBlockAlign", WORD), 
            ("wBitsPerSample", WORD), 
            ("cbSize", WORD) 
    ]

class WAVEHDR(Structure): 
    pass 
LPWAVEHDR = POINTER(WAVEHDR) 
WAVEHDR._fields_ = [ 
    ("lpData", LPSTR), 
    ("dwBufferLength", DWORD), 
    ("dwBytesRecorded", DWORD), 
    ("dwUser", POINTER(DWORD)), 
    ("dwFlags", DWORD), 
    ("dwLoops", DWORD), 
    ("lpNext", LPWAVEHDR), 
    ("reserved", POINTER(DWORD)) 
]

class SMPTE(Structure):
     _fields_ = [ 
        ("hour", BYTE),
        ("min", BYTE),
        ("sec", BYTE),
        ("frame", BYTE),
        ("fps", BYTE),
        ("dummy", BYTE),
        ("pad"[2], BYTE)
     ]

class MIDI(Structure):
     _fields_ = [ 
        ("songptrpos", DWORD)
     ]

class U(Union):
     _fields_ = [ 
        ("ms", DWORD),
        ("sample", DWORD),
        ("cb", DWORD),
        ("ticks", DWORD),
        ("smpte", SMPTE),
        ("midi", MIDI),
    ]

class MMTIME(Structure):
     _fields_ = [ 
        ("wType", UINT),
        ("u", U),
    ]


duration = 10

hWnd = windll.user32.CreateWindowExA(0,0xC018,0,0,0,0,0,0,0,0,0,0)
hdc = windll.user32.GetDC(hWnd)
    
pfd = PIXELFORMATDESCRIPTOR(0,1,PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,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)

SAMPLE_RATE     = 44100
SND_NUMCHANNELS = 2
SND_NUMSAMPLES  = math.ceil(duration*SAMPLE_RATE)
SND_NUMSAMPLESC = SND_NUMSAMPLES*SND_NUMCHANNELS
samples = (c_float * SND_NUMSAMPLESC)()

computeProg = glCreateProgram()
shader = glCreateShader(GL_COMPUTE_SHADER)
glShaderSource(shader, csh)
glCompileShader(shader)
if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE:
    raise RuntimeError(glGetShaderInfoLog(shader).decode())
glAttachShader(computeProg, shader)
glLinkProgram(computeProg)

vbo = glGenBuffers(1)
glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbo)
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(samples), None, GL_STATIC_DRAW)
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, vbo)
    
glUseProgram(computeProg)
glDispatchCompute(SND_NUMSAMPLESC // 128, 1, 1)
    
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(samples), byref(samples))    
    
wh = WAVEHDR(cast(samples, LPSTR), sizeof(samples)) 
wfx = WAVEFORMATEX(
    WAVE_FORMAT_IEEE_FLOAT,
    SND_NUMCHANNELS,
    SAMPLE_RATE,
    SAMPLE_RATE*sizeof(c_float)*SND_NUMCHANNELS,
    sizeof(c_float)*SND_NUMCHANNELS,
    sizeof(c_float)*8,
    0
)
wh = WAVEHDR(cast(samples, LPSTR), sizeof(samples)) 
hwo = HANDLE(0) 
winmm.waveOutOpen(byref(hwo), WAVE_MAPPER, byref(wfx), 0, 0, 0)
winmm.waveOutPrepareHeader(hwo, byref(wh), sizeof(WAVEHDR))
winmm.waveOutWrite(hwo, byref(wh), sizeof(WAVEHDR))

mmt = MMTIME(TIME_SAMPLES)
msg = MSG()
lpmsg = pointer(msg)
done = False
while done==False:
    while user32.PeekMessageA(lpmsg, 0, 0, 0, PM_REMOVE):
        if (msg.message == WM_NCLBUTTONDOWN and msg.wParam == HTCLOSE): done = True
        user32.DispatchMessageA(lpmsg)
    if(user32.GetAsyncKeyState(VK_ESCAPE)):  done = True
    winmm.waveOutGetPosition(hwo, byref(mmt), sizeof(mmt))
    sys.stdout.write("\r TIME : %f" %(mmt.u.sample / SAMPLE_RATE,))
    sys.stdout.flush()
    if (mmt.u.sample == SND_NUMSAMPLES): done = True
    
winmm.waveOutClose(hwo)
wglMakeCurrent(0, 0)
wglDeleteContext(hGLrc)
user32.ReleaseDC(hWnd, hdc)
user32.PostQuitMessage(0)
user32.DestroyWindow(hWnd)