├── .gitmodules ├── test ├── audio │ ├── arugh.wav │ ├── arughSt.wav │ ├── sample.wav │ ├── african_roomS.wav │ ├── audiodrivers.lua │ ├── multi_sndfile_player.lua │ ├── audiotest.lua │ ├── test-wav_async.lua │ ├── sndfile_player.lua │ ├── loopwave.lua │ └── multi_sndfile_player_gui.lua ├── video │ ├── lena.bmp │ └── test-bmp.lua └── threads │ ├── main.lua │ ├── main_atomic.lua │ └── main2.lua ├── generator ├── generator.bat ├── generator.lua └── cpp2ffi.lua ├── CMakeLists.txt ├── README.md └── sdlAudioPlayer.lua /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "SDL"] 2 | path = SDL 3 | url = https://github.com/libsdl-org/SDL 4 | -------------------------------------------------------------------------------- /test/audio/arugh.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL2/HEAD/test/audio/arugh.wav -------------------------------------------------------------------------------- /test/video/lena.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL2/HEAD/test/video/lena.bmp -------------------------------------------------------------------------------- /test/audio/arughSt.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL2/HEAD/test/audio/arughSt.wav -------------------------------------------------------------------------------- /test/audio/sample.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL2/HEAD/test/audio/sample.wav -------------------------------------------------------------------------------- /test/audio/african_roomS.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonoro1234/LuaJIT-SDL2/HEAD/test/audio/african_roomS.wav -------------------------------------------------------------------------------- /generator/generator.bat: -------------------------------------------------------------------------------- 1 | rem set your PATH if necessary for gcc and lua with: 2 | set PATH=C:\anima;C:\mingws\i686-7.2.0-release-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH% 3 | ::set PATH=%PATH%;C:\x86_64-8.1.0-release-posix-seh-rt_v6-rev0\mingw64\bin;C:\luaGL; 4 | ::gcc -E -dD -I ../SDL/include/ ../SDL/include/SDL.h 5 | luajit.exe ./generator.lua 6 | ::type tmp.lua glfw_base.lua > glfw.lua 7 | ::del tmp.lua 8 | 9 | cmd /k 10 | 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(sdl2_ffi) 2 | #to allow install from subdirectory 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | 6 | set(SDL_STATIC OFF CACHE INTERNAL "dont build static!") 7 | 8 | add_subdirectory(SDL EXCLUDE_FROM_ALL) 9 | set_target_properties(SDL2 PROPERTIES OUTPUT_NAME "SDL2") 10 | 11 | add_custom_target(snd2_ffi ALL) 12 | add_dependencies(snd2_ffi SDL2) 13 | 14 | install(TARGETS SDL2 RUNTIME DESTINATION ${LUAJIT_BIN} 15 | LIBRARY DESTINATION ${LUAJIT_BIN}) 16 | INSTALL(FILES sdl2_ffi.lua sdlAudioPlayer.lua DESTINATION ${LUAJIT_BIN}/lua) 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/video/test-bmp.lua: -------------------------------------------------------------------------------- 1 | local sdl = require 'sdl2_ffi' 2 | local ffi = require 'ffi' 3 | local C = ffi.C 4 | 5 | sdl.init(sdl.INIT_VIDEO) 6 | 7 | local window = sdl.createWindow("Hello Lena", 8 | sdl.WINDOWPOS_CENTERED, 9 | sdl.WINDOWPOS_CENTERED, 10 | 512, 11 | 512, 12 | sdl.WINDOW_SHOWN) 13 | 14 | local windowsurface = sdl.getWindowSurface(window) 15 | 16 | local image = sdl.LoadBMP("lena.bmp") 17 | 18 | sdl.upperBlit(image, nil, windowsurface, nil) 19 | 20 | sdl.updateWindowSurface(window) 21 | sdl.freeSurface(image) 22 | 23 | local running = true 24 | local event = ffi.new('SDL_Event') 25 | while running do 26 | while sdl.pollEvent(event) ~= 0 do 27 | if event.type == sdl.QUIT then 28 | running = false 29 | end 30 | end 31 | end 32 | 33 | sdl.destroyWindow(window) 34 | sdl.quit() 35 | -------------------------------------------------------------------------------- /test/threads/main.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl2_ffi" 2 | local ffi = require"ffi" 3 | 4 | 5 | if (sdl.init(sdl.INIT_VIDEO+sdl.INIT_TIMER) ~= 0) then 6 | 7 | print(string.format("Error: %s\n", sdl.getError())); 8 | return -1; 9 | end 10 | 11 | --/* Very simple thread - counts 0 to 9 delaying 1000ms between increments */ 12 | local function TestThread() 13 | local sdl = require"sdl2_ffi" 14 | return function(ptr) 15 | local cnt; 16 | for i = 0,9 do 17 | sdl.delay(1000); 18 | print(string.format("\nThread counter: %d", i)); 19 | cnt = i 20 | end 21 | return cnt; 22 | end 23 | end 24 | 25 | local threadReturnValue = ffi.new("int[1]") 26 | 27 | print("\nSimple SDL_CreateThread test:"); 28 | 29 | local thread = sdl.createThread(sdl.MakeThreadFunc(TestThread), "TestThread",nil) 30 | 31 | if (nil == thread) then 32 | local err = sdl.getError() 33 | print(string.format("\nSDL_CreateThread failed: %s\n",ffi.string(err))); 34 | else 35 | sdl.waitThread(thread, threadReturnValue); 36 | print(string.format("\nThread returned value: %d", threadReturnValue[0])); 37 | end 38 | 39 | -------------------------------------------------------------------------------- /test/audio/audiodrivers.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl2_ffi" 2 | local ffi = require"ffi" 3 | 4 | local function ffistring(cd) 5 | if not cd then 6 | return nil 7 | else 8 | return ffi.string(cd) 9 | end 10 | end 11 | if (sdl.init(sdl.INIT_AUDIO+sdl.INIT_TIMER) ~= 0) then 12 | print(string.format("Error: %s\n", sdl.GetError())); 13 | return -1; 14 | end 15 | --print("current driver:",ffistring(sdl.GetCurrentAudioDriver())) 16 | for i = 0, sdl.GetNumAudioDrivers()-1 do 17 | local driver_name = ffistring(sdl.GetAudioDriver(i)); 18 | print(i,driver_name) 19 | if (sdl.AudioInit(driver_name)<0) then 20 | local errstr = ffistring(sdl.GetError()) 21 | print(string.format("Audio driver failed to initialize: %s error: %s\n", driver_name,errstr)); 22 | else 23 | print("current driver:",ffistring(sdl.GetCurrentAudioDriver())) 24 | print"\tplaying audio devices:" 25 | for i=0,sdl.getNumAudioDevices(0)-1 do 26 | print('\t\tdevice:',i,ffistring(sdl.getAudioDeviceName(i,0))) 27 | end 28 | print"\trecording audio devices:" 29 | for i=0,sdl.getNumAudioDevices(1)-1 do 30 | print('\t\tdevice:',i,ffistring(sdl.getAudioDeviceName(i,1))) 31 | end 32 | sdl.AudioQuit(); 33 | end 34 | end 35 | 36 | sdl.Quit() -------------------------------------------------------------------------------- /test/audio/multi_sndfile_player.lua: -------------------------------------------------------------------------------- 1 | 2 | local sdl = require 'sdl2_ffi' 3 | local ffi = require 'ffi' 4 | --https://github.com/sonoro1234/LuaJIT-libsndfile 5 | local sndf = require"sndfile_ffi" 6 | local AudioPlayer = require"sdlAudioPlayer" 7 | 8 | -----------------------main-------------------------------------- 9 | local filename = "african_roomS.wav"; 10 | 11 | --/* Enable standard application logging */ 12 | sdl.LogSetPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 13 | 14 | --/* Load the SDL library */ 15 | if (sdl.Init(sdl.INIT_AUDIO + sdl.INIT_EVENTS) < 0) then 16 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 17 | return (1); 18 | end 19 | 20 | --copy specs from file 21 | local info = sndf.get_info(filename) 22 | local audioplayer,err = AudioPlayer({ 23 | --device = device_name, 24 | freq = info.samplerate, 25 | format = sdl.AUDIO_S16SYS, 26 | channels = info.channels, 27 | samples = 1024}) 28 | 29 | --insert several files 30 | for i=1,10 do 31 | --filename, level, timeoffset 32 | audioplayer:insert(filename,(11-i)*0.1,i*0.6) 33 | end 34 | --show them 35 | for node in audioplayer:nodes() do 36 | print("node",node.sf) 37 | end 38 | 39 | --play them 7 secs 40 | audioplayer:start() 41 | sdl.Delay(7000); 42 | --close 43 | audioplayer:close() 44 | sdl.Quit(); 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LuaJIT-SDL2 2 | autogenerated LuaJIT bindings for SDL2 2.30.3 3 | 4 | if interested in SDL3 there is also https://github.com/sonoro1234/LuaJIT-SDL3 5 | 6 | Set CMake LUAJIT_BIN to the LuaJIT directory for installing. 7 | 8 | See test folder examples for usage 9 | 10 | luajit-async (taken from https://github.com/sonoro1234/luajit-async) needs to be installed for providing lua functions that can be called from another thread (sdl.MakeAudioCallback and sdl.MakeThreadFunc). 11 | 12 | This repo is used in https://github.com/sonoro1234/LuaJIT-ImGui where you get very usefull GUI widgets. 13 | 14 | All above can be found at https://github.com/sonoro1234/anima 15 | 16 | # sdlAudioPlayer 17 | 18 | simple interface for playing sndfile files from disk 19 | 20 | ```lua 21 | local sndf = require"LuaJIT-libsndfile" 22 | local AudioPlayer = require"sdlAudioPlayer" 23 | 24 | --copy specs from file 25 | local info = sndf.get_info(filename) 26 | local audioplayer,err = AudioPlayer({ 27 | --device = device_name, --nil as device 28 | freq = info.samplerate, 29 | format = sdl.AUDIO_S16SYS, 30 | channels = info.channels, 31 | samples = 1024}) 32 | 33 | --insert several files 34 | for i=1,10 do 35 | --filename, level, timeoffset 36 | audioplayer:insert(filename,(11-i)*0.1,i*0.6) 37 | end 38 | --show them 39 | for node in audioplayer:nodes() do 40 | print("node",node.sf) 41 | end 42 | 43 | --play them 7 secs 44 | audioplayer:start() 45 | sdl.Delay(7000); 46 | --close 47 | audioplayer:close() 48 | ``` 49 | -------------------------------------------------------------------------------- /test/threads/main_atomic.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl2_ffi" 2 | local ffi = require"ffi" 3 | ffi.cdef[[void SDL_AtomicIncRef(SDL_atomic_t* a)]] 4 | if (sdl.init(sdl.INIT_VIDEO+sdl.INIT_TIMER) ~= 0) then 5 | 6 | print(string.format("Error: %s\n", sdl.getError())); 7 | return -1; 8 | end 9 | 10 | local function TestThread() 11 | local ffi = require"ffi" 12 | local sdl = require"sdl2_ffi" 13 | return function(ptr) 14 | local cnt; 15 | local atomic = ffi.cast("SDL_atomic_t *",ptr) 16 | for i = 0,99 do 17 | sdl.delay(5); 18 | sdl.AtomicAdd(atomic,1) 19 | local vv = sdl.AtomicGet(atomic) 20 | print(string.format("\nThread counter1: %d", vv)); 21 | cnt = i 22 | end 23 | return cnt; 24 | end 25 | end 26 | 27 | local function TestThread2() 28 | local ffi = require"ffi" 29 | local sdl = require"sdl2_ffi" 30 | return function(ptr) 31 | local cnt; 32 | local atomic = ffi.cast("SDL_atomic_t *",ptr) 33 | for i = 0,99 do 34 | sdl.delay(4); 35 | sdl.AtomicAdd(atomic,1) 36 | local vv = sdl.AtomicGet(atomic) 37 | print(string.format("\nThread counter2: %d", vv)); 38 | cnt = i 39 | end 40 | return cnt; 41 | end 42 | end 43 | 44 | 45 | --local data = ffi.new("SDL_atomic_t *atomic") 46 | local data = ffi.new("SDL_atomic_t[1]") 47 | local threadReturnValue = ffi.new("int[1]") 48 | 49 | print("\nSimple SDL_CreateThread test:"); 50 | 51 | local thread = sdl.createThread(sdl.MakeThreadFunc(TestThread), "TestThread",data[0]) 52 | local thread2 = sdl.createThread(sdl.MakeThreadFunc(TestThread2), "TestThread2",data[0]) 53 | 54 | 55 | if (nil == thread or nil==thread2) then 56 | local err = sdl.getError() 57 | print(string.format("\nSDL_CreateThread failed: %s\n",ffi.string(err))); 58 | else 59 | sdl.waitThread(thread, threadReturnValue); 60 | sdl.waitThread(thread2, nil); 61 | print(string.format("\nThread returned value: %d", threadReturnValue[0]),sdl.AtomicGet(data),"should be 200"); 62 | end 63 | 64 | sdl.Quit() 65 | 66 | -------------------------------------------------------------------------------- /test/threads/main2.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl2_ffi" 2 | local ffi = require"ffi" 3 | 4 | if (sdl.init(sdl.INIT_VIDEO+sdl.INIT_TIMER) ~= 0) then 5 | 6 | print(string.format("Error: %s\n", sdl.getError())); 7 | return -1; 8 | end 9 | 10 | local function TestThread() 11 | local ffi = require"ffi" 12 | local sdl = require"sdl2_ffi" 13 | return function(ptr) 14 | local cnt; 15 | local st = ffi.cast("struct {int data[1];SDL_mutex *mutex;}*",ptr) 16 | local data = st.data 17 | local mutex = st.mutex 18 | for i = 0,99 do 19 | sdl.delay(5); 20 | local ret = sdl.LockMutex(mutex) 21 | data[0] = data[0] + 1 22 | local vv = data[0] 23 | sdl.UnlockMutex(mutex) 24 | print(string.format("\nThread counter1: %d", vv)); 25 | cnt = i 26 | end 27 | return cnt; 28 | end 29 | end 30 | 31 | local function TestThread2() 32 | local ffi = require"ffi" 33 | local sdl = require"sdl2_ffi" 34 | return function(ptr) 35 | local cnt; 36 | local st = ffi.cast("struct {int data[1];SDL_mutex *mutex;}*",ptr) 37 | local data = st.data 38 | local mutex = st.mutex 39 | for i = 0,99 do 40 | sdl.delay(4); 41 | local ret = sdl.LockMutex(mutex) 42 | data[0] = data[0] + 1 43 | local vv = data[0] 44 | sdl.UnlockMutex(mutex) 45 | print(string.format("\nThread counter2: %d", vv)); 46 | cnt = i 47 | 48 | end 49 | return cnt; 50 | end 51 | end 52 | 53 | 54 | local data = ffi.new("struct {int data[1];SDL_mutex *mutex;}") 55 | data.mutex = sdl.createMutex() 56 | local threadReturnValue = ffi.new("int[1]") 57 | 58 | print("\nSimple SDL_CreateThread test:"); 59 | 60 | local thread = sdl.createThread(sdl.MakeThreadFunc(TestThread), "TestThread",data) 61 | local thread2 = sdl.createThread(sdl.MakeThreadFunc(TestThread2), "TestThread2",data) 62 | 63 | 64 | if (nil == thread or nil==thread2) then 65 | local err = sdl.getError() 66 | print(string.format("\nSDL_CreateThread failed: %s\n",ffi.string(err))); 67 | else 68 | sdl.waitThread(thread, threadReturnValue); 69 | sdl.waitThread(thread2, nil); 70 | print(string.format("\nThread returned value: %d", threadReturnValue[0]),data.data[0],"should be 200"); 71 | end 72 | 73 | sdl.DestroyMutex(data.mutex) 74 | sdl.Quit() 75 | 76 | -------------------------------------------------------------------------------- /test/audio/audiotest.lua: -------------------------------------------------------------------------------- 1 | local sdl = require"sdl2_ffi" 2 | local ffi = require"ffi" 3 | 4 | local oldffistring = ffi.string 5 | ffi.string = function(data) 6 | if data == nil then 7 | return "nil" 8 | else 9 | return oldffistring(data) 10 | end 11 | end 12 | 13 | local sampleHz = 48000 14 | 15 | local function AudioInit(udatacode) 16 | local ffi = require"ffi" 17 | local sin = math.sin 18 | ffi.cdef(udatacode) 19 | return function(ud,stream,len) 20 | 21 | local buf = ffi.cast("float*",stream) 22 | local udc = ffi.cast("MyUdata*",ud) 23 | local lenf = len/ffi.sizeof"float" 24 | 25 | for i=0,lenf-2,2 do 26 | local sample = sin(udc.Phase)*0.01 27 | udc.Phase = udc.Phase + udc.dPhase 28 | buf[i] = sample 29 | buf[i+1] = sample 30 | end 31 | end 32 | end 33 | 34 | local udatacode = [[typedef struct {double Phase;double dPhase;} MyUdata]] 35 | ffi.cdef(udatacode) 36 | local ud = ffi.new"MyUdata" 37 | local function setFreq(ff) 38 | sdl.LockAudio() 39 | ud.dPhase = 2 * math.pi * ff / sampleHz 40 | sdl.UnlockAudio() 41 | end 42 | 43 | local want = ffi.new"SDL_AudioSpec[1]" 44 | want[0].freq = sampleHz; 45 | want[0].format = sdl.AUDIO_F32; 46 | want[0].channels = 2; 47 | want[0].samples = 512 --4096; 48 | want[0].callback = sdl.MakeAudioCallback(AudioInit,udatacode) 49 | want[0].userdata = ud 50 | 51 | if (sdl.init(sdl.INIT_AUDIO+sdl.INIT_TIMER) ~= 0) then 52 | print(string.format("Error: %s\n", sdl.getError())); 53 | return -1; 54 | end 55 | print"audio drivers:" 56 | for i=0,sdl.getNumAudioDrivers()-1 do 57 | print('driver:',i, ffi.string(sdl.getAudioDriver(i))) 58 | end 59 | print("current driver",ffi.string(sdl.GetCurrentAudioDriver())) 60 | print"audio devices:" 61 | for i=0,sdl.getNumAudioDevices(0)-1 do 62 | print('device:',i,ffi.string(sdl.getAudioDeviceName(i,0))) 63 | end 64 | 65 | local desired 66 | ---[[ 67 | local desID 68 | while true do 69 | print"select audio device" 70 | desID = tonumber(io.read"*l") 71 | if desID and desID >= 0 and desID < sdl.getNumAudioDevices(0) then break end 72 | end 73 | desired = ffi.string(sdl.GetAudioDeviceName(desID,0)) 74 | print("desired",desired) 75 | --]] 76 | 77 | local dev = sdl.openAudioDevice(desired, 0, want, nil, 0) 78 | print("dev",dev) 79 | 80 | if (dev == 0) then 81 | sdl.log("Failed to open audio: %s", sdl.GetError()); 82 | else 83 | sdl.PauseAudioDevice(dev, 0); -- start audio playing. 84 | for i=1,100 do 85 | setFreq(math.random()*500 + 100) 86 | sdl.Delay(100) 87 | end 88 | sdl.PauseAudioDevice(dev, 1) 89 | sdl.CloseAudioDevice(dev); 90 | end 91 | 92 | sdl.Quit() -------------------------------------------------------------------------------- /test/audio/test-wav_async.lua: -------------------------------------------------------------------------------- 1 | -- mostly inspired by: 2 | -- http://www.gnurou.org/book/export/html/35 3 | -- example from https://github.com/torch/sdl2-ffi 4 | -- with async callback added 5 | 6 | local sdl = require 'sdl2_ffi' 7 | local ffi = require 'ffi' 8 | 9 | ffi.cdef[[ 10 | void *malloc(size_t size); 11 | void free(void *ptr); 12 | 13 | struct MySound { 14 | Uint8* data; 15 | Uint32 length; 16 | Uint32 pos; 17 | }; 18 | ]] 19 | 20 | local function mixaudio() 21 | local ffi = require"ffi" 22 | return function(userdata, stream, len) 23 | local sound = ffi.cast('struct {uint8_t* data;uint32_t length;uint32_t pos;}*', userdata) 24 | print(string.format("playing len=%d pos=%d\n", sound.length, sound.pos)) 25 | local tocopy = sound.length - sound.pos > len and len or sound.length - sound.pos 26 | ffi.copy(stream, sound.data + sound.pos, tocopy) 27 | sound.pos = sound.pos + tocopy 28 | end 29 | end 30 | 31 | 32 | if sdl.init(sdl.INIT_AUDIO) == -1 then 33 | error('could not initialize SDL') 34 | end 35 | 36 | for i=0,sdl.getNumAudioDrivers()-1 do 37 | print('driver:', ffi.string(sdl.getAudioDriver(i))) 38 | end 39 | 40 | local desired = ffi.new('SDL_AudioSpec') 41 | local obtained = ffi.new('SDL_AudioSpec') 42 | local soundfile = ffi.new('SDL_AudioSpec') 43 | 44 | local sound = ffi.new('struct MySound') 45 | 46 | desired.freq = 44100 47 | desired.format = sdl.AUDIO_U16 48 | desired.channels = 2 49 | desired.samples = 512 50 | desired.callback = sdl.MakeAudioCallback(mixaudio) 51 | desired.userdata = sound 52 | 53 | if sdl.openAudio(desired, obtained) ~= 0 then 54 | error(string.format('could not open audio device: %s', ffi.string(sdl.getError()))) 55 | end 56 | 57 | print(string.format('obtained parameters: format=%s, channels=%s freq=%s size=%s bytes', 58 | bit.band(obtained.format, 0xff), obtained.channels, obtained.freq, obtained.samples)) 59 | 60 | do 61 | local sounddata = ffi.new('Uint8*[1]') 62 | local soundlength = ffi.new('Uint32[1]') 63 | local ret = sdl.LoadWAV("arugh.wav", soundfile, sounddata, soundlength) 64 | print("ret",ret) 65 | if ret == nil then 66 | error(string.format('could not read audio file: %s', ffi.string(sdl.getError()))) 67 | end 68 | sound.data = sounddata[0] 69 | sound.length = soundlength[0] 70 | end 71 | 72 | local cvt = ffi.new('SDL_AudioCVT') 73 | if sdl.buildAudioCVT(cvt, soundfile.format, soundfile.channels, soundfile.freq, 74 | obtained.format, obtained.channels, obtained.freq) < 0 then 75 | error('could not build audio converter') 76 | end 77 | 78 | cvt.buf = ffi.C.malloc(sound.length * cvt.len_mult) 79 | cvt.len = sound.length 80 | ffi.copy(cvt.buf, sound.data, sound.length) 81 | 82 | if sdl.convertAudio(cvt) ~= 0 then 83 | error(string.format('problem during audio conversion: %s', sdl.getError())) 84 | end 85 | 86 | sdl.freeWAV(sound.data) 87 | sound.data = ffi.C.malloc(cvt.len_cvt) 88 | ffi.copy(sound.data, cvt.buf, cvt.len_cvt) 89 | ffi.C.free(cvt.buf) 90 | 91 | sound.length = cvt.len_cvt 92 | print(string.format('converted audio size: %s bytes', sound.length)) 93 | 94 | sdl.pauseAudio(0) 95 | 96 | jit.off() 97 | 98 | while sound.pos < sound.length do end 99 | 100 | jit.on() 101 | 102 | sdl.pauseAudio(1) 103 | 104 | sdl.closeAudio() 105 | 106 | sdl.quit() 107 | -------------------------------------------------------------------------------- /test/audio/sndfile_player.lua: -------------------------------------------------------------------------------- 1 | --[[/* 2 | Copyright (C) 1997-2018 Sam Lantinga 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely. 11 | */ 12 | 13 | /* Program to load a sndfile and playing it using SDL audio */ 14 | 15 | --]] 16 | 17 | local sdl = require 'sdl2_ffi' 18 | local ffi = require 'ffi' 19 | --https://github.com/sonoro1234/LuaJIT-libsndfile 20 | local sf = require"sndfile_ffi" 21 | 22 | 23 | --will run in a separate thread and lua state 24 | local function fillerup(spec) 25 | 26 | local ffi = require"ffi" 27 | local sdl = require"sdl2_ffi" 28 | local sndf = require"sndfile_ffi" 29 | 30 | local spec = ffi.cast("SDL_AudioSpec*",spec) 31 | 32 | local typebuffer,lenfac = sdl.audio_buffer_type(spec[0]) 33 | 34 | local bufpointer = typebuffer.."*" 35 | local readfunc = "readf_"..typebuffer 36 | print("Init audio:",spec[0].freq,bufpointer,readfunc) 37 | 38 | return function(ud,stream,len) 39 | local sf = ffi.cast("SNDFILE_ref*",ud) 40 | local lenf = len*lenfac 41 | assert(lenf == math.floor(lenf)) 42 | streamf = ffi.cast(bufpointer,stream) 43 | sf[readfunc](sf,streamf,lenf) 44 | end 45 | end 46 | 47 | 48 | local filename = "sample.wav"; 49 | 50 | --/* Enable standard application logging */ 51 | sdl.LogSetPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 52 | 53 | --/* Load the SDL library */ 54 | if (sdl.Init(sdl.INIT_AUDIO + sdl.INIT_EVENTS) < 0) then 55 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 56 | return (1); 57 | end 58 | 59 | 60 | local sf1 = sf.Sndfile(filename) 61 | local spec = ffi.new"SDL_AudioSpec[1]" 62 | spec[0].freq = sf1:samplerate(); 63 | spec[0].format = sdl.AUDIO_S16; --try others 64 | spec[0].channels = sf1:channels(); 65 | spec[0].callback = sdl.MakeAudioCallback(fillerup,spec)--; 66 | spec[0].userdata = sf1 67 | 68 | --/* Show the list of available drivers */ 69 | sdl.Log("Available audio drivers:"); 70 | for i = 0,sdl.GetNumAudioDrivers()-1 do 71 | sdl.Log("%i: %s",ffi.new("int", i), sdl.GetAudioDriver(i)); 72 | end 73 | 74 | sdl.Log("Using audio driver: %s\n", sdl.GetCurrentAudioDriver()); 75 | 76 | --/* Initialize fillerup() variables */ 77 | local device = sdl.OpenAudioDevice(nil, sdl.FALSE, spec, nil, 0); 78 | if (device==0) then 79 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", sdl.GetError()); 80 | sf1:close() 81 | quit(2); 82 | end 83 | 84 | --/* Let the audio run */ 85 | sdl.PauseAudioDevice(device, sdl.FALSE); 86 | 87 | 88 | local done = false; 89 | while (not done) do 90 | local event = ffi.new"SDL_Event" 91 | 92 | while (sdl.PollEvent(event) > 0) do 93 | if (event.type == sdl.QUIT) then 94 | done = true; 95 | end 96 | end 97 | sdl.Delay(100); 98 | end 99 | 100 | 101 | --/* Clean up on signal */ 102 | sdl.CloseAudioDevice(device); 103 | sf1:close() 104 | sdl.Quit(); 105 | 106 | -------------------------------------------------------------------------------- /test/audio/loopwave.lua: -------------------------------------------------------------------------------- 1 | --[[/* 2 | Copyright (C) 1997-2018 Sam Lantinga 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely. 11 | */ 12 | 13 | /* Program to load a wave file and loop playing it using SDL audio */ 14 | 15 | /* loopwaves.c is much more robust in handling WAVE files -- 16 | This is only for simple WAVEs 17 | */ 18 | --]] 19 | 20 | local sdl = require 'sdl2_ffi' 21 | local ffi = require 'ffi' 22 | 23 | 24 | local ud_code = [[ 25 | typedef struct 26 | { 27 | SDL_AudioSpec spec[1]; 28 | Uint8 *sound[1]; /* Pointer to wave data */ 29 | Uint32 soundlen[1]; /* Length of wave data */ 30 | int soundpos; /* Current play position */ 31 | } wave; 32 | ]] 33 | 34 | ffi.cdef(ud_code) 35 | local wave = ffi.new"wave" 36 | local device; 37 | 38 | --/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ 39 | function quit(rc) 40 | sdl.Quit(); 41 | os.exit(rc); 42 | end 43 | 44 | function close_audio() 45 | if (device ~= 0) then 46 | sdl.CloseAudioDevice(device); 47 | device = 0; 48 | end 49 | end 50 | 51 | 52 | function open_audio() 53 | 54 | --/* Initialize fillerup() variables */ 55 | device = sdl.OpenAudioDevice(nil, sdl.FALSE, wave.spec, nil, 0); 56 | if (device==0) then 57 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", sdl.GetError()); 58 | sdl.FreeWAV(wave.sound); 59 | quit(2); 60 | end 61 | 62 | 63 | --/* Let the audio run */ 64 | sdl.PauseAudioDevice(device, sdl.FALSE); 65 | end 66 | 67 | function reopen_audio() 68 | close_audio(); 69 | open_audio(); 70 | end 71 | 72 | 73 | local function fillerup(udcode) 74 | local ffi = require"ffi" 75 | local sdl = require"sdl2_ffi" 76 | local waveptr = ffi.new"Uint8[1]" 77 | ffi.cdef(udcode) 78 | return function(ud,stream,len) 79 | 80 | local waveleft; 81 | local wave = ffi.cast("wave*",ud) 82 | --/* Set up the pointers */ 83 | waveptr = wave.sound[0] + wave.soundpos; 84 | waveleft = wave.soundlen[0] - wave.soundpos; 85 | 86 | --/* Go! */ 87 | while (waveleft <= len) do 88 | sdl.C.SDL_memcpy(stream, waveptr, waveleft); 89 | stream = stream + waveleft; 90 | len = len - waveleft; 91 | waveptr = wave.sound[0]; 92 | waveleft = wave.soundlen[0]; 93 | wave.soundpos = 0; 94 | end 95 | sdl.C.SDL_memcpy(stream, waveptr, len); 96 | wave.soundpos = wave.soundpos + len; 97 | end 98 | end 99 | local done = false; 100 | 101 | 102 | 103 | local filename = ffi.new"char[4096]"; 104 | 105 | --/* Enable standard application logging */ 106 | sdl.LogSetPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 107 | 108 | --/* Load the SDL library */ 109 | if (sdl.Init(sdl.INIT_AUDIO + sdl.INIT_EVENTS) < 0) then 110 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 111 | return (1); 112 | end 113 | 114 | sdl.strlcpy(filename, "sample.wav", ffi.sizeof(filename)); 115 | 116 | --/* Load the wave file into memory */ 117 | if (sdl.LoadWAV(filename, wave.spec, wave.sound, wave.soundlen) == nil) then 118 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, sdl.GetError()); 119 | quit(1); 120 | end 121 | 122 | wave.spec[0].callback = sdl.MakeAudioCallback(fillerup,ud_code) 123 | wave.spec[0].userdata = wave 124 | 125 | --/* Show the list of available drivers */ 126 | sdl.Log("Available audio drivers:"); 127 | for i = 0,sdl.GetNumAudioDrivers()-1 do 128 | sdl.Log("%i: %s",ffi.new("int", i), sdl.GetAudioDriver(i)); 129 | end 130 | 131 | sdl.Log("Using audio driver: %s\n", sdl.GetCurrentAudioDriver()); 132 | 133 | open_audio(); 134 | 135 | sdl.FlushEvents(sdl.AUDIODEVICEADDED, sdl.AUDIODEVICEREMOVED); 136 | 137 | 138 | while (not done) do 139 | local event = ffi.new"SDL_Event" 140 | 141 | while (sdl.PollEvent(event) > 0) do 142 | if (event.type == sdl.QUIT) then 143 | done = true; 144 | end 145 | if ((event.type == sdl.AUDIODEVICEADDED and not event.adevice.iscapture==1) or 146 | (event.type == sdl.AUDIODEVICEREMOVED and not event.adevice.iscapture==1 and event.adevice.which == device)) then 147 | reopen_audio(); 148 | end 149 | end 150 | sdl.Delay(100); 151 | end 152 | 153 | 154 | --/* Clean up on signal */ 155 | close_audio(); 156 | sdl.FreeWAV(wave.sound); 157 | sdl.Quit(); 158 | return (0); 159 | -------------------------------------------------------------------------------- /test/audio/multi_sndfile_player_gui.lua: -------------------------------------------------------------------------------- 1 | --jit.off(true,true) 2 | 3 | local sdl = require 'sdl2_ffi' 4 | local ffi = require 'ffi' 5 | --https://github.com/sonoro1234/LuaJIT-libsndfile 6 | local sndf = require"sndfile_ffi" 7 | 8 | local AudioPlayer = require"sdlAudioPlayer" 9 | --to show pointer values 10 | ffi.cdef[[int snprintf ( char * s, size_t n, const char * format, ... ); 11 | int sprintf ( char * str, const char * format, ... ); 12 | ]] 13 | --------------------------will run in audio thread after playing files 14 | local delaycdef = [[typedef struct delay{double feedback[1];double delay[1];double maxdelay;} delay]] 15 | ffi.cdef(delaycdef) 16 | local fxdata = ffi.new("delay",{ffi.new("double[1]",0.0),ffi.new("double[1]",1),2}) 17 | 18 | local function delayfunc(data,code,typebuffer,nchannels,spec) 19 | local ffi = require"ffi" 20 | ffi.cdef(code) 21 | data = ffi.cast("delay*",data) 22 | local index = 0 23 | local lenb = math.floor(spec.freq*nchannels*data.maxdelay) 24 | local buffer = ffi.new(typebuffer.."[?]",lenb) 25 | 26 | return function(streamf,lenf,streamTime) 27 | local lenbe = math.floor(spec.freq*nchannels*data.delay[0]) 28 | local j 29 | for i=0,(lenf*nchannels)-1 do 30 | j = index + i 31 | if j > lenbe-1 then j = j - lenbe end 32 | streamf[i] = streamf[i] + buffer[j] 33 | buffer[j] = streamf[i] *data.feedback[0] 34 | end 35 | index = index + lenf*nchannels 36 | if index > lenbe-1 then index = index - lenbe end 37 | end 38 | end 39 | 40 | ------------------------------------------------------------------------ 41 | -----------------------main-------------------------------------- 42 | 43 | local ig = require"imgui.sdl" 44 | 45 | local filename = "african_roomS.wav"; 46 | --local filename = "arugh.wav" --"sample.wav"; 47 | 48 | --/* Enable standard application logging */ 49 | sdl.LogSetPriority(sdl.LOG_CATEGORY_APPLICATION, sdl.LOG_PRIORITY_INFO); 50 | if (sdl.Init(sdl.INIT_VIDEO + sdl.INIT_AUDIO + sdl.INIT_EVENTS) < 0) then 51 | sdl.LogError(sdl.LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", sdl.GetError()); 52 | return (1); 53 | end 54 | 55 | --copy specs from file 56 | local info = sndf.get_info(filename) 57 | local audioplayer,err = AudioPlayer({ 58 | --device = device_name, 59 | freq = info.samplerate, 60 | format = sdl.AUDIO_S16SYS, 61 | channels = info.channels, 62 | samples = 1024}, 63 | delayfunc,fxdata,delaycdef) 64 | 65 | if not audioplayer then print(err) end 66 | 67 | print"--------------wanted" 68 | audioplayer.wanted_spec[0]:print() 69 | print("---------------opened device",device) 70 | audioplayer.obtained_spec[0]:print() 71 | 72 | ---------------------------------------------------- 73 | 74 | --insert 3 files 75 | --level 1, timeoffset 0 76 | audioplayer:insert(filename,1,0.0) 77 | audioplayer:insert("arughSt.wav",1,0.01) 78 | --will not load, diferent samplerate and channels 79 | local node2 = audioplayer:insert("arughSt.wav",0,0.75) 80 | --audioplayer:insert("arughSt.wav",0.7,2.75) 81 | --audioplayer:insert("arughSt.wav",0,4.75) 82 | audioplayer:insert(filename,1,2) 83 | 84 | for node in audioplayer:nodes() do 85 | print("node",node.sf) 86 | end 87 | 88 | --audioplayer:erase(node2) 89 | print"after erase" 90 | for node in audioplayer:nodes() do 91 | print("node",node.sf) 92 | end 93 | 94 | --audioplayer:record("recording.wav",sndf.SF_FORMAT_WAV+sndf.SF_FORMAT_FLOAT) 95 | print("audioplayer.recordfile",audioplayer.recordfile) 96 | 97 | print"--------------------------------------" 98 | -------------------------------------------------- 99 | 100 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_FLAGS, sdl.GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); 101 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_CORE); 102 | sdl.gL_SetAttribute(sdl.GL_DOUBLEBUFFER, 1); 103 | sdl.gL_SetAttribute(sdl.GL_DEPTH_SIZE, 24); 104 | sdl.gL_SetAttribute(sdl.GL_STENCIL_SIZE, 8); 105 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_MAJOR_VERSION, 3); 106 | sdl.gL_SetAttribute(sdl.GL_CONTEXT_MINOR_VERSION, 2); 107 | 108 | local window = sdl.createWindow("ImGui SDL2+OpenGL3 example", sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, 700, 500, sdl.WINDOW_OPENGL+sdl.WINDOW_RESIZABLE); 109 | 110 | 111 | local gl_context = sdl.gL_CreateContext(window); 112 | sdl.gL_SetSwapInterval(1); -- Enable vsync 113 | 114 | local ig_Impl = ig.Imgui_Impl_SDL_opengl3() 115 | 116 | ig_Impl:Init(window, gl_context) 117 | 118 | local igio = ig.GetIO() 119 | 120 | local done = false; 121 | local streamtime = ffi.new("float[1]") 122 | while (not done) do 123 | 124 | local event = ffi.new"SDL_Event" 125 | while (sdl.pollEvent(event) ~=0) do 126 | ig.lib.ImGui_ImplSDL2_ProcessEvent(event); 127 | if (event.type == sdl.QUIT) then 128 | done = true; 129 | end 130 | if (event.type == sdl.WINDOWEVENT and event.window.event == sdl.WINDOWEVENT_CLOSE and event.window.windowID == sdl.getWindowID(window)) then 131 | done = true; 132 | end 133 | end 134 | 135 | sdl.gL_MakeCurrent(window, gl_context); 136 | 137 | 138 | ig_Impl:NewFrame() 139 | -------audio gui 140 | if ig.Button("start") then 141 | audioplayer:start() 142 | end 143 | if ig.Button("stop") then 144 | audioplayer:stop() 145 | end 146 | 147 | streamtime[0] = audioplayer:get_stream_time() 148 | --print(streamtime[0], audioplayer:get_stream_time()) 149 | if ig.SliderFloat("time",streamtime,0,15) then 150 | audioplayer:set_stream_time(streamtime[0]) 151 | end 152 | 153 | ig.SliderScalar("delay",ig.lib.ImGuiDataType_Double,fxdata.delay,ffi.new("double[1]",0),ffi.new("double[1]",fxdata.maxdelay)) 154 | 155 | ig.SliderScalar("feedback",ig.lib.ImGuiDataType_Double,fxdata.feedback,ffi.new("double[1]",0),ffi.new("double[1]",1)) 156 | 157 | if ig.Button("nodes") then 158 | print"----------nodes---------------" 159 | print(audioplayer.root,tonumber(audioplayer.root),audioplayer.root.next[0]) 160 | for node in audioplayer:nodes() do 161 | print(node,node.next[0],node.level,node.timeoffset) 162 | print(node.sf,node.sf:samplerate(),node.sf:channels(),node.sf:format()) 163 | end 164 | end 165 | 166 | if ig.Button("insert") then 167 | audioplayer:insert(filename,1,streamtime[0]) 168 | end 169 | ig.SameLine() 170 | if ig.Button("insert arugh") then 171 | audioplayer:insert("arughSt.wav",0.5,streamtime[0]) 172 | end 173 | if audioplayer.recordfile~=nil then 174 | ig.SameLine() 175 | if ig.Button("close record") then 176 | audioplayer.recordfile:close() 177 | end 178 | end 179 | 180 | ---[[ 181 | 182 | local format = string.format 183 | local cbuf = ffi.new"char[201]" 184 | local level = ffi.new("float[1]") 185 | ig.PushItemWidth(80) 186 | for node in audioplayer:nodes() do 187 | ig.PushID_Ptr(node) 188 | ffi.C.sprintf(cbuf,"%p",node) 189 | ig.Text(cbuf);ig.SameLine() 190 | level[0] = node.level 191 | if ig.SliderFloat("level",level,0,1) then 192 | node.level = level[0] 193 | end 194 | ig.SameLine();ig.Text(format("time:%4.2f",node.timeoffset)) 195 | ig.SameLine();ig.Text(format("srate:%4.0f",node.sf:samplerate()));ig.SameLine(); 196 | 197 | if ig.SmallButton("delete") then 198 | audioplayer:erase(node) 199 | end 200 | ig.PopID() 201 | end 202 | ig.PopItemWidth() 203 | 204 | --]] 205 | -- end audio gui 206 | ig_Impl:Render() 207 | sdl.gL_SwapWindow(window); 208 | end 209 | 210 | audioplayer:close() 211 | ig_Impl:destroy() 212 | 213 | sdl.gL_DeleteContext(gl_context); 214 | sdl.destroyWindow(window); 215 | sdl.Quit(); 216 | 217 | -------------------------------------------------------------------------------- /generator/generator.lua: -------------------------------------------------------------------------------- 1 | local ffi = require"ffi" 2 | local ffi_cdef = function(code) 3 | local ret,err = pcall(ffi.cdef,code) 4 | if not ret then 5 | local lineN = 1 6 | for line in code:gmatch("([^\n\r]*)\r?\n") do 7 | print(lineN, line) 8 | lineN = lineN + 1 9 | end 10 | print(err) 11 | error"bad cdef" 12 | end 13 | end 14 | 15 | local cp2c = require"cpp2ffi" 16 | ------------------------------------------------------ 17 | local cdefs = {} 18 | 19 | cp2c.save_data("./outheader.h",[[#include ]]) 20 | local pipe,err = io.popen([[gcc -E -dD -I ../SDL/include/ ./outheader.h]],"r") 21 | if not pipe then 22 | error("could not execute gcc "..err) 23 | end 24 | 25 | local defines = {} 26 | for line in cp2c.location(pipe,{[[SDL.-]]},defines) do 27 | --local line = strip(line) 28 | table.insert(cdefs,line) 29 | end 30 | pipe:close() 31 | os.remove"./outheader.h" 32 | 33 | 34 | local txt = table.concat(cdefs,"\n") 35 | --cp2c.save_data("./cpreout.txt",txt) 36 | 37 | local itemsarr,items = cp2c.parseItems(txt) 38 | 39 | print"items" 40 | for k,v in pairs(items) do 41 | print(k,#v) 42 | end 43 | 44 | --make new cdefs 45 | local cdefs = {} 46 | for k,v in ipairs(itemsarr) do 47 | -- if v.item:match"_Static_assert" then 48 | -- for kk,vv in pairs(v) do print(kk,vv) end 49 | -- break 50 | -- end 51 | if v.re_name ~= "functionD_re" then --skip defined funcs 52 | if v.re_name=="function_re" then 53 | --skip CreateThread and _Static_assert 54 | if not v.item:match("CreateThread") and not v.item:match"_Static_assert" then 55 | local item = v.item 56 | if item:match("^%s*extern") then 57 | item = item:gsub("^%s*extern%s*(.+)","\n%1") 58 | end 59 | table.insert(cdefs,item) 60 | end 61 | else 62 | table.insert(cdefs,v.item) 63 | end 64 | end 65 | end 66 | ------------------------------ 67 | local deftab = {} 68 | local ffi = require"ffi" 69 | ffi_cdef(table.concat(cdefs,"")) 70 | local wanted_strings = {"^SDL","^AUDIO_","^KMOD_","^RW_"} 71 | for i,v in ipairs(defines) do 72 | local wanted = false 73 | for _,wan in ipairs(wanted_strings) do 74 | if (v[1]):match(wan) then wanted=true; break end 75 | end 76 | if wanted then 77 | local lin = "static const int "..v[1].." = " .. v[2] .. ";" 78 | local ok,msg = pcall(function() return ffi.cdef(lin) end) 79 | if not ok then 80 | print("skipping def",lin) 81 | print(msg) 82 | else 83 | table.insert(deftab,lin) 84 | end 85 | end 86 | end 87 | 88 | 89 | local special_win = [[ 90 | typedef unsigned long (__cdecl *pfnSDL_CurrentBeginThread) (void *, unsigned, 91 | unsigned (__stdcall *func)(void *), void *arg, 92 | unsigned, unsigned *threadID); 93 | typedef void (__cdecl *pfnSDL_CurrentEndThread)(unsigned code); 94 | 95 | uintptr_t __cdecl _beginthreadex(void *_Security,unsigned _StackSize,unsigned (__stdcall *_StartAddress) (void *),void *_ArgList,unsigned _InitFlag,unsigned *_ThrdAddr); 96 | void __cdecl _endthreadex(unsigned _Retval); 97 | 98 | static const int SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED_MASK; 99 | SDL_Thread * SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data,pfnSDL_CurrentBeginThread bf,pfnSDL_CurrentEndThread ef); 100 | SDL_Thread * SDL_CreateThreadWithStackSize(int ( * fn) (void *),const char *name, const size_t stacksize, void *data,pfnSDL_CurrentBeginThread bf,pfnSDL_CurrentEndThread ef); 101 | ]] 102 | 103 | local special =[[ 104 | static const int SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED_MASK; 105 | SDL_Thread * SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data); 106 | SDL_Thread * SDL_CreateThreadWithStackSize(int ( * fn) (void *),const char *name, const size_t stacksize, void *data); 107 | ]] 108 | 109 | 110 | -----------make test 111 | local funcnames = {} 112 | --[[ 113 | for i,v in ipairs(items[function_re]) do 114 | local funcname = v:match("([%w_]+)%s*%(") 115 | if not funcname then print(v) end 116 | table.insert(funcnames,"if not pcall(function() local nn=M.C."..funcname.." end) then print('bad','"..funcname.."') end") 117 | end 118 | --]] 119 | 120 | 121 | --output sdl2_ffi 122 | local sdlstr = [[ 123 | local ffi = require"ffi" 124 | 125 | --uncomment to debug cdef calls]].. 126 | "\n---[["..[[ 127 | 128 | --local ffi_cdef = ffi.cdef 129 | local ffi_cdef = function(code) 130 | local ret,err = pcall(ffi.cdef,code) 131 | if not ret then 132 | local lineN = 1 133 | for line in code:gmatch("([^\n\r]*)\r?\n") do 134 | print(lineN, line) 135 | lineN = lineN + 1 136 | end 137 | print(err) 138 | error"bad cdef" 139 | end 140 | end 141 | ]].."--]]"..[[ 142 | 143 | ffi_cdef]].."[["..table.concat(cdefs,"").."]]"..[[ 144 | 145 | ffi_cdef]].."[["..table.concat(deftab,"\n").."]]"..[[ 146 | 147 | if ffi.os == 'Windows' then 148 | ffi_cdef]].."[["..special_win.."]]"..[[ 149 | 150 | else 151 | ffi_cdef]].."[["..special.."]]"..[[ 152 | 153 | end 154 | 155 | local lib = ffi.load"SDL2" 156 | 157 | local M = {C=lib} 158 | 159 | if ffi.os == "Windows" then 160 | 161 | function M.createThread(a,b,c) 162 | return lib.SDL_CreateThread(a,b,c,ffi.C._beginthreadex, ffi.C._endthreadex) 163 | end 164 | 165 | function M.createThreadWithStackSize(a,b,c,d) 166 | return lib.SDL_CreateThreadWithStackSize(a,b,c,d,ffi.C._beginthreadex, ffi.C._endthreadex) 167 | end 168 | 169 | end 170 | 171 | function M.LoadBMP(file) 172 | return M.LoadBMP_RW(M.RWFromFile(file, 'rb'), 1) 173 | end 174 | function M.LoadWAV(file, spec, audio_buf, audio_len) 175 | return M.LoadWAV_RW(M.RWFromFile(file, "rb"), 1, spec, audio_buf, audio_len) 176 | end 177 | function M.SaveBMP(surface, file) 178 | return M.SaveBMP_RW(surface, M.RWFromFile(file, 'wb'), 1) 179 | end 180 | 181 | local AudioSpecs = {} 182 | AudioSpecs.__index = AudioSpecs 183 | function AudioSpecs:print() 184 | print(string.format('spec parameters: \nfreq=%s, \nformat=%s, \nformat bits=%s, \nis float %s,\nendianess=%d, \nis signed %s, \nchannels=%s \nsilence=%s, \nsamples=%s bytes,\nsize=%s bytes', self.freq,self.format, bit.band(self.format, 0xff),tostring(bit.band(0x1000,self.format)>0), bit.band(0x100,self.format) , tostring(bit.band(0xF000,self.format)>0),self.channels, self.silence, self.samples, self.size)) 185 | end 186 | ffi.metatype("SDL_AudioSpec",AudioSpecs) 187 | 188 | --function returning typebuffer,lenfac,nchannels from spec 189 | function M.audio_buffer_type(spec) 190 | local nchannels = spec.channels 191 | local bitsize = bit.band(spec.format,0xff) 192 | local isfloat = bit.band(spec.format,0x100) 193 | local typebuffer 194 | if isfloat>0 then 195 | if bitsize == 32 then typebuffer = "float" 196 | else error("unknown float buffer type bits:"..tostring(bitsize)) end 197 | else 198 | if bitsize == 16 then typebuffer = "short" 199 | elseif bitsize == 32 then typebuffer = "int" 200 | else error("unknown buffer type bits:"..tostring(bitsize)) end 201 | end 202 | local lenfac = 1/(ffi.sizeof(typebuffer)*nchannels) 203 | return typebuffer,lenfac,nchannels 204 | end 205 | 206 | 207 | local callback_t 208 | local states_anchor = {} 209 | function M.MakeAudioCallback(func, ...) 210 | if not callback_t then 211 | local CallbackFactory = require "lj-async.callback" 212 | callback_t = CallbackFactory("void(*)(void*,uint8_t*,int)") --"SDL_AudioCallback" 213 | end 214 | local cb = callback_t(func, ...) 215 | table.insert(states_anchor,cb) 216 | return cb:funcptr(), cb 217 | end 218 | local threadfunc_t 219 | function M.MakeThreadFunc(func, ...) 220 | if not threadfunc_t then 221 | local CallbackFactory = require "lj-async.callback" 222 | threadfunc_t = CallbackFactory("int(*)(void*)") 223 | end 224 | local cb = threadfunc_t(func, ...) 225 | table.insert(states_anchor,cb) 226 | return cb:funcptr(), cb 227 | end 228 | 229 | setmetatable(M,{ 230 | __index = function(t,k) 231 | local ok,ptr = pcall(function(str) return lib["SDL_"..str] end,k) 232 | if not ok then ok,ptr = pcall(function(str) return lib[str] end,k) end --some defines without SDL_ 233 | if not ok then --torch sdl2 calling 234 | local str2 = "SDL_"..string.upper(k:sub(1,1))..k:sub(2) 235 | ok,ptr = pcall(function(str) return lib[str] end,str2) 236 | end 237 | if not ok then error(k.." not found") end 238 | rawset(M, k, ptr) 239 | return ptr 240 | end 241 | }) 242 | 243 | 244 | ]]..table.concat(funcnames,"\n")..[[ 245 | 246 | return M 247 | ]] 248 | 249 | cp2c.save_data("./sdl2_ffi.lua",sdlstr) 250 | cp2c.copyfile("./sdl2_ffi.lua","../sdl2_ffi.lua") 251 | -------------------------------------------------------------------------------- /sdlAudioPlayer.lua: -------------------------------------------------------------------------------- 1 | local ffi = require"ffi" 2 | local sdl = require"sdl2_ffi" 3 | local sndf = require"sndfile_ffi" 4 | 5 | --------------will run in a same thread and different lua state and return the callback 6 | local function AudioInit(audioplayer,audioplayercdef,postfunc,postdata,postcode) 7 | 8 | local ffi = require"ffi" 9 | local sdl = require"sdl2_ffi" 10 | local sndf = require"sndfile_ffi" 11 | 12 | 13 | ffi.cdef(audioplayercdef) 14 | audioplayer = ffi.cast("audioplayer*",audioplayer) 15 | local root = audioplayer.root 16 | local spec 17 | 18 | local typebuffer,lenfac,nchannels 19 | local timefac 20 | local bufpointer 21 | local readfunc,writefunc 22 | local postfuncS 23 | 24 | --wait until device is opened to define locals declared above 25 | local function setspecs() 26 | 27 | spec = audioplayer.obtained_spec[0] 28 | --print("spec.format", spec.format, spec.channels) 29 | typebuffer,lenfac,nchannels = sdl.audio_buffer_type(spec) 30 | timefac = 1/spec.freq 31 | bufpointer = typebuffer.."*" 32 | readfunc = "readf_"..typebuffer 33 | writefunc = "writef_"..typebuffer 34 | 35 | postfuncS = postfunc(postdata,postcode,typebuffer,nchannels,spec) 36 | 37 | end 38 | 39 | local floor = math.floor 40 | -- this is the real callback 41 | return function(ud,stream,len) 42 | 43 | if not spec then setspecs() end 44 | 45 | local streamTime = audioplayer.streamTime 46 | local lenf = len*lenfac 47 | assert(lenf == floor(lenf)) 48 | local windowsize = lenf * timefac 49 | sdl.memset(stream, 0, len) 50 | local streamf = ffi.cast(bufpointer,stream) 51 | ffi.fill(stream,len) 52 | local readbuffer = ffi.new(typebuffer.."[?]",lenf*nchannels) 53 | local sf_node = root 54 | while true do 55 | if sf_node.next~=nil then 56 | sf_node = sf_node.next[0] 57 | local sf = sf_node.sf 58 | if sf.resampler~=nil then 59 | sf = sf.resampler 60 | end 61 | 62 | if sf_node.timeoffset <= streamTime then --already setted 63 | local readen = tonumber(sf[readfunc](sf,readbuffer,lenf)) 64 | for i=0,(readen*nchannels)-1 do 65 | streamf[i] = streamf[i] + readbuffer[i]*sf_node.level 66 | end 67 | elseif sf_node.timeoffset < streamTime + windowsize then --set it here 68 | --print"sett--------------------------------" 69 | local frames = floor((streamTime + windowsize - sf_node.timeoffset) * spec.freq) 70 | local res = sf:seek( 0, sndf.SEEK_SET) 71 | local readen = tonumber(sf[readfunc](sf,readbuffer,frames)) 72 | local j=0 73 | for i=(lenf - frames)*nchannels,((readen+lenf-frames)*nchannels)-1 do 74 | --for i=(lenf - frames)*nchannels,((lenf)*nchannels)-1 do 75 | streamf[i] = streamf[i] + readbuffer[j]*sf_node.level 76 | j = j + 1 77 | end 78 | end 79 | else break end 80 | 81 | end 82 | 83 | postfuncS(streamf,lenf,streamTime) 84 | if audioplayer.recordfile~= nil then 85 | audioplayer.recordfile[writefunc](audioplayer.recordfile,streamf,lenf) 86 | end 87 | audioplayer.streamTime = streamTime + lenf*timefac 88 | end 89 | end 90 | --------------------------------------------------- 91 | ---------------------------------------------audioplayer interface 92 | local audioplayercdef = [[ 93 | typedef struct sf_node sf_node; 94 | struct sf_node 95 | { 96 | SNDFILE_ref *sf; 97 | double level; 98 | double timeoffset; 99 | sf_node *next; 100 | } sf_node; 101 | 102 | typedef struct audioplayer 103 | { 104 | SDL_AudioSpec wanted_spec[1]; 105 | SDL_AudioSpec obtained_spec[1]; 106 | sf_node root; 107 | SNDFILE_ref *recordfile; 108 | double streamTime; 109 | SDL_AudioDeviceID device; 110 | src_callback_t resampler_input_cb; 111 | } audioplayer; 112 | ]] 113 | 114 | 115 | ffi.cdef(audioplayercdef) 116 | local ap_resampler_input_cb 117 | local ancla_nodes = {} 118 | local ancla_resam = {} 119 | local AudioPlayer_mt = {} 120 | AudioPlayer_mt.__index = AudioPlayer_mt 121 | function AudioPlayer_mt:__new(t,postfunc,postdata,postcode) 122 | local postfunc = postfunc or function() return function() end end 123 | local ap = ffi.new("audioplayer") 124 | assert(ap.root.next == nil) 125 | local spec = ap.wanted_spec[0] 126 | spec.freq = t.freq or 44100 127 | spec.format = t.format or 0 128 | spec.channels = t.channels or 2 129 | spec.samples = t.samples or 0 130 | local callback, cbmaker = sdl.MakeAudioCallback(AudioInit,ap,audioplayercdef,postfunc,postdata,postcode) 131 | spec.callback = callback 132 | ap_resampler_input_cb = cbmaker:additional_cb(function() 133 | local sndf = require"sndfile_ffi" 134 | return sndf.resampler_input_cb 135 | end,"long (*) (void *cb_data, float **data)")--"src_callback_t") 136 | ap.resampler_input_cb = ap_resampler_input_cb 137 | ap.device = sdl.OpenAudioDevice(t.device, sdl.FALSE, ap.wanted_spec, ap.obtained_spec,sdl.AUDIO_ALLOW_FORMAT_CHANGE); 138 | if ap.device == 0 then 139 | local err = sdl.GetError() 140 | return nil, err~=nil and ffi.string(err) or "unknown error opening device" 141 | end 142 | ffi.gc(ap,self.close) 143 | return ap 144 | end 145 | function AudioPlayer_mt:close() 146 | ffi.gc(self,nil) 147 | for node in self:nodes() do 148 | node.sf:close() 149 | end 150 | if self.recordfile ~=nil then 151 | self.recordfile:close() 152 | end 153 | sdl.CloseAudioDevice(self.device); 154 | 155 | end 156 | function AudioPlayer_mt:get_stream_time() 157 | self:lock() 158 | local ret = self.streamTime 159 | self:unlock() 160 | return ret 161 | end 162 | function AudioPlayer_mt:set_stream_time(time) 163 | self:lock() 164 | self.streamTime = time 165 | local sf_node = self.root 166 | while true do 167 | sf_node = sf_node.next[0] 168 | if sf_node == nil then break end 169 | local sf = sf_node.sf 170 | if sf.resampler~=nil then 171 | sf = sf.resampler 172 | end 173 | if sf_node.timeoffset <= time then 174 | local frames = math.floor((time - sf_node.timeoffset) * sf_node.sf:samplerate()) 175 | local res = sf:seek( frames, sndf.SEEK_SET) ; 176 | end 177 | end 178 | self:unlock() 179 | end 180 | function AudioPlayer_mt:lock() 181 | sdl.LockAudioDevice(self.device) 182 | end 183 | function AudioPlayer_mt:unlock() 184 | sdl.UnlockAudioDevice(self.device) 185 | end 186 | function AudioPlayer_mt:start() 187 | --sdl.LockAudio() 188 | sdl.PauseAudioDevice(self.device, 0) 189 | --sdl.UnlockAudio() 190 | end 191 | function AudioPlayer_mt:stop() 192 | --sdl.LockAudio() 193 | sdl.PauseAudioDevice(self.device, 1) 194 | --sdl.UnlockAudio() 195 | end 196 | 197 | function AudioPlayer_mt:insert(filename,level,timeoffset) 198 | level = level or 1 199 | timeoffset = timeoffset or 0 200 | local sf = sndf.Sndfile(filename) 201 | --check channels and samplerate 202 | if sf:channels() ~= self.obtained_spec[0].channels then 203 | print(filename,"has wrong number of channels",sf:channels()) 204 | sf:close() 205 | return nil 206 | end 207 | local selfkey = tostring(self) 208 | if sf:samplerate() ~= self.obtained_spec[0].freq then 209 | local resamp = sf:resampler_create(nil, nil,ap_resampler_input_cb) 210 | resamp:set_ratio(self.obtained_spec[0].freq/sf:samplerate()) 211 | local anchor = ancla_resam[selfkey] or {} 212 | ancla_resam[selfkey] = anchor 213 | table.insert(anchor,resamp) 214 | end 215 | local node = ffi.new"sf_node[1]" 216 | local anchor = ancla_nodes[selfkey] or {} 217 | ancla_nodes[selfkey] = anchor 218 | table.insert(anchor,node) 219 | node[0].sf = sf 220 | node[0].level = level 221 | node[0].timeoffset = timeoffset 222 | 223 | node[0].next = self.root.next 224 | self:lock() 225 | self.root.next = node 226 | self:unlock() 227 | return node[0] 228 | end 229 | local recordfile_anchor 230 | function AudioPlayer_mt:record(filename,format) 231 | assert(self.recordfile==nil,"AudioPlayer already has recording file.") 232 | local sf = sndf.Sndfile(filename,"w",self.obtained_spec[0].freq,self.obtained_spec[0].channels,format) 233 | recordfile_anchor = sf 234 | self.recordfile = sf 235 | return sf 236 | end 237 | function AudioPlayer_mt:erase(node) 238 | self:lock() 239 | local sf_node = self.root 240 | local anchor = ancla_nodes[tostring(self)] 241 | assert(anchor) 242 | while true do 243 | local prev = sf_node 244 | sf_node = sf_node.next[0] 245 | if sf_node == nil then break end 246 | if sf_node == node then 247 | --remove from ancla_nodes 248 | for i,nodeptr in ipairs(anchor) do 249 | if nodeptr[0]==node then 250 | table.remove(anchor,i) 251 | break 252 | end 253 | end 254 | prev.next = sf_node.next 255 | node.sf:close() 256 | break 257 | end 258 | end 259 | self:unlock() 260 | end 261 | function AudioPlayer_mt:nodes() 262 | local cur_node = self.root 263 | return function() 264 | local nextnode = cur_node.next[0] 265 | if nextnode == nil then return nil end 266 | cur_node = nextnode 267 | return nextnode 268 | end 269 | end 270 | 271 | local AudioPlayer = ffi.metatype("audioplayer",AudioPlayer_mt) 272 | return AudioPlayer -------------------------------------------------------------------------------- /generator/cpp2ffi.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local function str_split(str, pat) 4 | local t = {} 5 | local fpat = "(.-)" .. pat 6 | local last_end = 1 7 | local s, e, cap = str:find(fpat, 1) 8 | while s do 9 | table.insert(t,cap) 10 | last_end = e+1 11 | s, e, cap = str:find(fpat, last_end) 12 | end 13 | if last_end <= #str then 14 | cap = str:sub(last_end) 15 | table.insert(t, cap) 16 | elseif str:sub(-1)==pat then 17 | table.insert(t, "") 18 | end 19 | return t 20 | end 21 | local function split_comment(line) 22 | local comment = line:match("(%s*//.*)") or "" 23 | line = line:gsub("%s*//.*","") 24 | line = line:gsub("%s*$","") 25 | return line,comment 26 | end 27 | M.split_comment = split_comment 28 | local function strip(cad) 29 | return cad:gsub("^%s*(.-)%s*$","%1") --remove initial and final spaces 30 | end 31 | local function strip_end(cad) 32 | return cad:gsub("^(.-)%s*$","%1") --remove initial and final spaces 33 | end 34 | local function clean_spaces(cad) 35 | cad = strip(cad) 36 | cad = cad:gsub("%s+"," ") --not more than one space 37 | cad = cad:gsub("%s*([%(%),=])%s*","%1") --not spaces with ( , ) 38 | return cad 39 | end 40 | function strsplit(str, pat) 41 | local t = {} 42 | local fpat = "(.-)" .. pat 43 | local last_end = 1 44 | local s, e, cap = str:find(fpat, 1) 45 | while s do 46 | table.insert(t,cap) 47 | last_end = e+1 48 | s, e, cap = str:find(fpat, last_end) 49 | end 50 | if last_end <= #str then 51 | cap = str:sub(last_end) 52 | table.insert(t, cap) 53 | elseif str:sub(-1)==pat then 54 | table.insert(t, "") 55 | end 56 | return t 57 | end 58 | -------------------------------------------------------------------------- 59 | local function save_data(filename,...) 60 | local file,err = io.open(filename,"w") 61 | if not file then error(err) end 62 | for i=1, select('#', ...) do 63 | local data = select(i, ...) 64 | file:write(data) 65 | end 66 | file:close() 67 | end 68 | M.save_data = save_data 69 | ---------------------------------------- 70 | local function read_data(filename) 71 | local hfile,err = io.open(filename,"r") 72 | if not hfile then error(err) end 73 | local hstrfile = hfile:read"*a" 74 | hfile:close() 75 | return hstrfile 76 | end 77 | M.read_data = read_data 78 | ------------------------------------------------------------ 79 | local function copyfile(src,dst,blocksize) 80 | blocksize = blocksize or 1024*4 81 | print( "copyfile", src, dst) 82 | local srcf, err = io.open(src,"rb") 83 | if not srcf then error(err) end 84 | local dstf, err = io.open(dst,"wb") 85 | if not dstf then error(err) end 86 | while true do 87 | local data = srcf:read(blocksize) 88 | if not data then break end 89 | dstf:write(data) 90 | end 91 | srcf:close() 92 | dstf:close() 93 | end 94 | M.copyfile = copyfile 95 | 96 | --gives the re table 97 | local function getRE() 98 | local res = { 99 | function_re = "^([^;{}]+%b()[\n%s]*;)%s*", 100 | function_re = "^([^;{}]+%b()[\n%s%w]*;)", --const at the end 101 | struct_re = "^([^;{}]-struct[^;{}]-%b{}[%s%w_%(%)]*;)", 102 | enum_re = "^([^;{}]-enum[^;{}]-%b{}[%s%w_%(%)]*;)", 103 | union_re = "^([^;{}]-union[^;{}]-%b{}[%s%w_%(%)]*;)", 104 | structenum_re = "^([^;{}]-%b{}[%s%w_%(%)]*;)", 105 | namespace_re = "^([^;{}]-namespace[^;{}]-%b{})", 106 | class_re = "^([^;{}]-class[^;{}]-%b{}%s*;)", 107 | typedef_re = "^\n*(typedef[^;]+;)", 108 | functypedef_re = "^\n*%s*(typedef[%w%s%*_]+%(%s*%*%s*[%w_]+%s*%)%s*%b()%s*;)", 109 | functypedef_re = "^\n*%s*(typedef[%w%s%*_]+%([^*]*%*%s*[%w_]+%s*%)%s*%b()%s*;)", 110 | vardef_re = "^\n*([^;{}%(%)]+;)", 111 | functionD_re = "^([^;{}]-%b()[\n%s%w]*%b{})", 112 | --functionD_re = "^([^;{}]-%b()[^{}%(%)]*%b{})", 113 | functype_re = "^%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)%s*;" 114 | } 115 | 116 | local resN = {"functypedef_re","functype_re","function_re","functionD_re","struct_re","enum_re","union_re","namespace_re","class_re","typedef_re","vardef_re"} 117 | 118 | return res,resN 119 | end 120 | M.getRE = getRE 121 | --takes preprocesed file in table cdefsor and returns items 122 | local function parseItems(txt,dumpit) 123 | --dumpit = true 124 | local res,resN = getRE() 125 | 126 | local ini = 1 127 | local item 128 | local items = {} 129 | local itemarr = {} 130 | while true do 131 | local found = false 132 | for ire,re_name in ipairs(resN) do 133 | local re = res[re_name] 134 | local i,e = txt:find(re,ini) 135 | if i then 136 | item = txt:sub(i,e) 137 | --if re~=functionD_re then --skip defined functions 138 | item = item:gsub("extern __attribute__%(%(dllexport%)%) ","") 139 | table.insert(itemarr,{re_name=re_name,item=item}) 140 | --end 141 | items[re_name] = items[re_name] or {} 142 | table.insert(items[re_name],item) 143 | found = true 144 | ini = e + 1 145 | if dumpit then 146 | print(item) 147 | print(ire,"------------------------------------------------------") 148 | end 149 | break 150 | end 151 | end 152 | if not found then 153 | if not (ini >= #txt) then 154 | local rest = txt:sub(ini) 155 | local onlyspaces = rest:match("^%s*$") 156 | if not onlyspaces then 157 | print(ini,#txt); 158 | print(txt); 159 | print(item) 160 | error"parseItems error" 161 | end 162 | end 163 | break 164 | end 165 | end 166 | return itemarr,items 167 | end 168 | M.parseItems = parseItems 169 | local function name_overloadsAlgo(v) 170 | local aa = {} 171 | local bb = {} 172 | local done = {} 173 | local maxnum = 0 174 | for i,t in ipairs(v) do 175 | bb[i] = "" 176 | local signature = t.signature:sub(2,-2) -- without parenthesis 177 | aa[i] = {} 178 | local num = 1 179 | --for typec in t.signature:gmatch(".-([^,%(%s]+)[,%)]") do 180 | --for typec in t.signature:gmatch(".-([^,%(]+)[,%)]") do 181 | --for typec in signature:gmatch(".-([^,]+),?") do 182 | for typec in signature:gsub("(%(.-%))", function(x) return x:gsub(",","\0") end):gmatch(".-([^,]+),?") do 183 | --typec = typec:gsub 184 | aa[i][num] = typec:gsub("%z+", ",") 185 | num = num + 1 186 | end 187 | num = num - 1 188 | maxnum = (num > maxnum) and num or maxnum 189 | end 190 | 191 | for l=1,maxnum do 192 | local keys = {} 193 | local diferent = true 194 | local equal = true 195 | for i=1,#v do 196 | aa[i][l] = aa[i][l] or "nil" 197 | keys[aa[i][l]] = 1 + (aa[i][l] and keys[aa[i][l]] or 0) 198 | if not done[i] then 199 | for j=i+1,#v do 200 | if not done[j] then 201 | if aa[i][l] == aa[j][l] then 202 | diferent = false 203 | else 204 | equal = false 205 | end 206 | end 207 | end 208 | end 209 | end 210 | if not equal then -- not all the same 211 | for i=1,#v do 212 | if not done[i] then 213 | bb[i] = bb[i]..(aa[i][l]=="nil" and "" or aa[i][l]) 214 | if keys[aa[i][l]] == 1 then 215 | done[i] = true 216 | end 217 | end 218 | end 219 | end 220 | end 221 | return aa,bb 222 | end 223 | local function typetoStr(typ) 224 | --typ = typ:gsub("[^%(%)]+%(%*?(.+)%).+","%1") -- funcs 225 | typ = typ:gsub("[^%(%)]+%(%*?(.+)%).+","FnPtr") 226 | typ = typ:gsub("[%w_]+%[(%d*)%]","arr%1") 227 | typ = typ:gsub("%*","Ptr") 228 | typ = typ:gsub("void","") 229 | typ = typ:gsub("unsigned%s","u") 230 | typ = typ:gsub("const%s","")--"c") 231 | typ = typ:gsub("%s+","_") 232 | typ = typ:gsub("charPtr","Str") 233 | typ = typ:gsub("int","Int") 234 | typ = typ:gsub("bool","Bool") 235 | typ = typ:gsub("float","Float") 236 | typ = typ:gsub("uInt","Uint") 237 | typ = typ:gsub("ImGui","") 238 | typ = typ:gsub("Im","") 239 | typ = typ:gsub("[<>]","") 240 | return typ 241 | end 242 | local function parseFunction(self,stname,lineorig,namespace) 243 | line = clean_spaces(lineorig) 244 | --move * 245 | line = line:gsub("%s*%*","%*") 246 | line = line:gsub("%*([%w_])","%* %1") 247 | line = line:gsub("(%(%*)%s","%1") 248 | 249 | --print(line) 250 | --clean implemetation 251 | line = line:gsub("%s*%b{}","") 252 | --clean attribute 253 | line = line:gsub("%s*__attribute__%b()","") 254 | --clean static and inline and mutable 255 | line = line:gsub("static","") 256 | line = line:gsub("inline","") 257 | line = line:gsub("mutable","") 258 | --skip operator 259 | if line:match("operator") then return end 260 | --skip template 261 | if line:match("template") then return end 262 | 263 | local ret = line:match("([^%(%)]+[%*%s])%s?~?[_%w]+%b()") 264 | local funcname, args = line:match("(~?[_%w]+)%s*(%b())") 265 | 266 | if not args then 267 | print"not gettint args in" 268 | print(line,lineorig) 269 | print(funcname,"args",args) 270 | end 271 | 272 | local argscsinpars = args:gsub("(=[^,%(%)]*)(%b())","%1") 273 | argscsinpars = argscsinpars:gsub("(=[^,%(%)]*)([,%)])","%2") 274 | -- if argscsinpars:match("&") then 275 | -- for arg in argscsinpars:gmatch("[%(,]*([^,%(%)]+)[%),]") do 276 | -- if arg:match("&") and not arg:match("const") then 277 | -- print(funcname,argscsinpars) 278 | -- end 279 | -- end 280 | -- end 281 | --argscsinpars = argscsinpars:gsub("&","") 282 | 283 | --for _,ttype in ipairs(self.templatedTypes) do 284 | --local template = argscsinpars:match(ttype.."%s*<(.+)>") 285 | local ttype,template = argscsinpars:match("([^%s,%(%)]+)%s*<(.+)>") 286 | local te="" 287 | if template then 288 | te = template:gsub("%s","_") 289 | te = te:gsub("%*","Ptr") 290 | self.templates[ttype] = self.templates[ttype] or {} 291 | self.templates[ttype][template] = te 292 | end 293 | --end 294 | argscsinpars = argscsinpars:gsub("<([%w_%*]+)>","_"..te) --ImVector 295 | 296 | local argsArr = {} 297 | local functype_re = "^%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)" 298 | local functype_reex = "^(%s*[%w%s%*]+)%(%*([%w_]+)%)(%([^%(%)]*%))" 299 | local functype_arg_rest = "^(%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)),*(.*)" 300 | local rest = argscsinpars:sub(2,-2) --strip () 301 | 302 | while true do 303 | --local tt = strsplit(rest,",") 304 | --for ii,arg in ipairs(tt) do 305 | --for arg in argscsinpars:gmatch("[%(,]*([^,%(%)]+)[%),]") do 306 | if rest == "void" then break end 307 | local type,name,retf,sigf 308 | local arg,restt = rest:match(functype_arg_rest) 309 | if arg then 310 | local t1,namef,t2 = arg:match(functype_reex) 311 | type=t1.."(*)"..t2;name=namef 312 | retf = t1 313 | sigf = t2 314 | rest = restt 315 | else 316 | arg,restt = rest:match(",*([^,%(%)]+),*(.*)") 317 | if not arg then break end 318 | rest = restt 319 | if arg:match("&") and arg:match("const") then 320 | arg = arg:gsub("&","") 321 | end 322 | if arg:match("%.%.%.") then 323 | type="...";name="..." 324 | else 325 | type,name = arg:match("(.+)%s([^%s]+)") 326 | end 327 | 328 | if not type or not name then 329 | print("failure arg detection",funcname,type,name,argscsinpars,arg) 330 | 331 | else 332 | if name:match"%*" then print("**",funcname) end 333 | --float name[2] to float[2] name 334 | local siz = name:match("(%[%d*%])") 335 | if siz then 336 | type = type..siz 337 | name = name:gsub("(%[%d*%])","") 338 | end 339 | end 340 | end 341 | table.insert(argsArr,{type=type,name=name,ret=retf,signature=sigf}) 342 | if arg:match("&") and not arg:match("const") then 343 | --only post error if not manual 344 | local cname = self.getCname(stname,funcname) --cimguiname 345 | if not self.manuals[cname] then 346 | print("reference to no const arg in",funcname,argscsinpars) 347 | end 348 | end 349 | end 350 | argscsinpars = argscsinpars:gsub("&","") 351 | 352 | local signature = argscsinpars:gsub("([%w%s%*_]+)%s[%w_]+%s*([,%)])","%1%2") 353 | signature = signature:gsub("%s*([,%)])","%1") --space before , and ) 354 | signature = signature:gsub(",%s*",",")--space after , 355 | signature = signature:gsub("([%w_]+)%s[%w_]+(%[%d*%])","%1%2") -- float[2] 356 | signature = signature:gsub("(%(%*)[%w_]+(%)%([^%(%)]*%))","%1%2") --func defs 357 | 358 | local call_args = argscsinpars:gsub("([%w_]+%s[%w_]+)%[%d*%]","%1") --float[2] 359 | call_args = call_args:gsub("%(%*([%w_]+)%)%([^%(%)]*%)"," %1") --func type 360 | call_args = call_args:gsub("[^%(].-([%w_]+)%s*([,%)])","%1%2") 361 | 362 | if not ret and stname then --must be constructors 363 | if not (stname == funcname or "~"..stname==funcname) then --break end 364 | print("false constructor:",line); 365 | print("b2:",ret,stname,funcname,args) 366 | return --are function defs 367 | end 368 | end 369 | 370 | local cimguiname = self.getCname(stname,funcname) 371 | table.insert(self.funcdefs,{stname=stname,funcname=funcname,args=args,argsc=argscsinpars,signature=signature,cimguiname=cimguiname,call_args=call_args,ret =ret,comment=comment}) 372 | local defsT = self.defsT 373 | defsT[cimguiname] = defsT[cimguiname] or {} 374 | table.insert(defsT[cimguiname],{}) 375 | local defT = defsT[cimguiname][#defsT[cimguiname]] 376 | defT.defaults = {} 377 | --for k,def in args:gmatch("([%w%s%*_]+)=([%w_%(%)%s,%*]+)[,%)]") do 378 | --for k,def in args:gmatch("([%w_]+)=([%w_%(%)%s,%*%.%-]+)[,%)]") do 379 | for k,def in args:gmatch('([%w_]+)=([%w_%(%)%s,%*%.%-%+%%"]+)[,%)]') do 380 | defT.defaults[k]=def 381 | end 382 | defT.namespace = namespace 383 | defT.cimguiname = cimguiname 384 | defT.stname = stname 385 | defT.funcname = funcname 386 | defT.argsoriginal = args 387 | defT.args=argscsinpars 388 | defT.signature = signature 389 | defT.call_args = call_args 390 | defT.isvararg = signature:match("%.%.%.%)$") 391 | defT.location = locat 392 | defT.comment = "" --comment 393 | defT.argsT = argsArr 394 | if self.get_manuals(defT) then 395 | defT.manual = true 396 | end 397 | if ret then 398 | defT.ret = clean_spaces(ret:gsub("&","*")) 399 | defT.retref = ret:match("&") 400 | -- if defT.ret=="ImVec2" or defT.ret=="ImVec4" or defT.ret=="ImColor" then 401 | -- defT.ret = defT.ret.."_Simple" 402 | -- end 403 | end 404 | defsT[cimguiname][signature] = defT 405 | end 406 | local function itemsCount(items) 407 | print"------------items" 408 | local res,resN = getRE() 409 | for k,v in pairs(resN) do 410 | local its = items[v] 411 | print(v,its and #its or 0) 412 | end 413 | end 414 | 415 | local function AdjustArguments(FP) 416 | for fun,defs in pairs(FP.defsT) do 417 | --struct function but no constructors 418 | if defs[1].stname~="" and defs[1].ret then 419 | --print("adjusting",fun) 420 | for i,def in ipairs(defs) do 421 | local empty = def.args:match("^%(%)") --no args 422 | --local ptret = def.retref and "&" or "" 423 | def.args = def.args:gsub("^%(","("..def.stname.."* self"..(empty and "" or ",")) 424 | table.insert(def.argsT,1,{type=def.stname.."*",name="self"}) 425 | end 426 | end 427 | end 428 | end 429 | local function ADDnonUDT(FP) 430 | local defsT = FP.defsT 431 | local newcdefs = {} 432 | --for cimguiname,defs in pairs(defsT) do 433 | --for i,defT in ipairs(defs) do 434 | --local t = {cimguiname=cimguiname,signature=defT.signature,ret=defT.ret} 435 | for numcdef,t in ipairs(FP.funcdefs) do 436 | if t.cimguiname then 437 | local cimf = defsT[t.cimguiname] 438 | local defT = cimf[t.signature] 439 | --if UDT return generate nonUDT version 440 | local isUDT = false 441 | for _,udt_ret in ipairs(FP.UDTs) do 442 | if udt_ret == defT.ret then isUDT=true;break end 443 | end 444 | --if defT.ret=="ImVec2" or defT.ret=="ImVec4" or defT.ret=="ImColor" then 445 | if isUDT then 446 | --passing as a pointer arg 447 | local defT2 = {} 448 | --first strings 449 | for k,v in pairs(defT) do 450 | defT2[k] = v 451 | end 452 | --then argsT table 453 | defT2.argsT = {{type=defT.ret.."*",name="pOut"}} 454 | for k,v in ipairs(defT.argsT) do 455 | table.insert(defT2.argsT,{type=v.type,name=v.name}) 456 | end 457 | local comma = (#defT.argsT > 0) and "," or "" 458 | defT2.args = "("..defT.ret.." *pOut"..comma..defT.args:sub(2) 459 | defT2.ret = "void" 460 | defT2.ov_cimguiname = (defT2.ov_cimguiname or defT2.cimguiname).."_nonUDT" 461 | defT2.nonUDT = 1 462 | defT2.retref = nil 463 | defsT[t.cimguiname][#defsT[t.cimguiname] + 1] = defT2 464 | defsT[t.cimguiname][t.signature.."nonUDT"] = defT2 465 | table.insert(newcdefs,{stname=t.stname,funcname=t.funcname,args=args,argsc=argscsinpars,signature=t.signature.."nonUDT",cimguiname=t.cimguiname,call_args=call_args,ret =t.ret,comment=comment}) 466 | --converting to Simple type---------------------------------------------------- 467 | local defT3 = {} 468 | --first strings 469 | for k,v in pairs(defT) do 470 | defT3[k] = v 471 | end 472 | --then argsT table 473 | defT3.argsT = {} 474 | for k,v in ipairs(defT.argsT) do 475 | table.insert(defT3.argsT,{type=v.type,name=v.name}) 476 | end 477 | local comma = (#defT.argsT > 0) and "," or "" 478 | --defT3.args = "("..defT.ret.." *pOut"..comma..defT.args:sub(2) 479 | defT3.ret = defT.ret.."_Simple" 480 | defT3.retorig = defT.ret 481 | defT3.ov_cimguiname = (defT3.ov_cimguiname or defT3.cimguiname).."_nonUDT2" 482 | defT3.nonUDT = 2 483 | defT3.retref = nil 484 | defsT[t.cimguiname][#defsT[t.cimguiname] + 1] = defT3 485 | defsT[t.cimguiname][t.signature.."nonUDT2"] = defT3 486 | table.insert(newcdefs,{stname=t.stname,funcname=t.funcname,args=args,argsc=argscsinpars,signature=t.signature.."nonUDT2",cimguiname=t.cimguiname,call_args=call_args,ret =t.ret,comment=comment}) 487 | end 488 | end 489 | --end 490 | end 491 | for i,v in ipairs(newcdefs) do 492 | table.insert(FP.funcdefs,v) 493 | end 494 | end 495 | 496 | local function ADDdestructors(FP) 497 | local defsT = FP.defsT 498 | local newcdefs = {} 499 | --TODO add constructor = true 500 | for numcdef,t in ipairs(FP.funcdefs) do 501 | newcdefs[#newcdefs+1] = t 502 | if t.cimguiname then 503 | local defT = defsT[t.cimguiname] 504 | --local defT = cimf[t.signature] 505 | --for fname,defT in pairs(FP.defsT) do 506 | if not defT[1].ret and not defT[1].constructor then --if constructor not processed 507 | if defT[1].funcname:match("~") then 508 | defsT[t.cimguiname] = nil --clear destructor 509 | newcdefs[#newcdefs] = nil 510 | else 511 | for j,cons in ipairs(defT) do 512 | cons.constructor = true 513 | end 514 | assert(defT[1].stname==defT[1].funcname) 515 | local def = {} 516 | def.stname = defT[1].stname 517 | def.ret = "void" 518 | def.ov_cimguiname = def.stname.."_destroy" 519 | def.cimguiname = def.ov_cimguiname 520 | def.destructor = true 521 | def.args = "("..def.stname.."* self)" 522 | def.call_args = "(self)" 523 | def.signature = "("..def.stname.."*)" 524 | def.defaults = {} 525 | def.argsT = {{type=def.stname.."*",name="self"}} 526 | defsT[def.ov_cimguiname] = {def} 527 | defsT[def.ov_cimguiname][def.signature] = def 528 | newcdefs[#newcdefs+1]={stname=def.stname,funcname=def.ov_cimguiname,args=def.args,signature=def.signature,cimguiname=def.cimguiname,call_args=def.call_args,ret =def.ret} 529 | end 530 | end 531 | end 532 | end 533 | FP.funcdefs = newcdefs 534 | end 535 | 536 | --only basic ending 537 | local c_types = { 538 | ["char"]=true, 539 | ["int"]=true, 540 | ["float"]=true, 541 | ["double"]=true, 542 | ["short"]=true, 543 | ["long"]=true, 544 | ["signed"]=true, 545 | ["unsigned"]=true, 546 | ["size_t"]=true, 547 | ["ptrdiff_t"]=true, 548 | } 549 | local function check_arg_detection(fdefs,typedefs) 550 | print"-----------------check arg detection---------------------------" 551 | for k,defT in pairs(fdefs) do 552 | for i,def in ipairs(defT) do 553 | for j,arg in ipairs(def.argsT) do 554 | --check name is not type, which happens in declaration without name 555 | if arg.name=="*" or not arg.type or not arg.name or c_types[arg.name] or typedefs[arg.name] then 556 | print("bad argument name",arg.name, "in",def.funcname,def.args) 557 | end 558 | end 559 | end 560 | end 561 | print"-----------------end check arg detection-----------------------" 562 | end 563 | local function printItems(items) 564 | local function printItemsKind(items,kind) 565 | local res,resN = getRE() 566 | print("---------------------------------------",kind) 567 | local someit = items[kind] 568 | if someit then 569 | for i=1,#someit do print(someit[i]) end 570 | end 571 | end 572 | local res,resN = getRE() 573 | for k,v in pairs(resN) do 574 | printItemsKind(items,v) 575 | end 576 | end 577 | ------------- 578 | function M.Parser() 579 | local par = {} 580 | local cdefs ={} 581 | local itemsarr={} 582 | local items = {} 583 | par.itemsarr = itemsarr 584 | par.items = items 585 | par.defsT = {} 586 | par.funcdefs = {} 587 | par.embeded_structs = {} 588 | par.inerstructs = {} 589 | par.templates = {} 590 | par.typedefs_dict = {} 591 | par.cname_overloads = {} 592 | par.manuals = {} 593 | par.UDTs = {} 594 | 595 | function par:insert(line) 596 | table.insert(cdefs,line) 597 | end 598 | function par.getCname(stname,funcname) 599 | if #stname == 0 then return funcname end --top level 600 | local pre = stname.."_" 601 | return pre..funcname 602 | end 603 | function par.getCname_overload(stname,funcname,signature) 604 | local cname = par.getCname(stname,funcname) 605 | local ov_cname = par.cname_overloads[cname] and par.cname_overloads[cname][signature] --or cname 606 | return ov_cname 607 | end 608 | function par.get_manuals(def) 609 | return par.manuals[def.ov_cimguiname] or par.manuals[def.cimguiname] 610 | end 611 | function par:do_parse() 612 | self:parseItems() 613 | self:parseFunctions() 614 | self:compute_overloads() 615 | end 616 | function par:parseItems() 617 | --typedefs dictionary 618 | for i,line in ipairs(cdefs) do 619 | if line:match("typedef") then 620 | line = clean_spaces(line) 621 | local value,key = line:match("typedef%s+(.+)%s+([%w_]+);") 622 | if key and value then 623 | self.typedefs_dict[key] = value 624 | else --try function typedef 625 | local key = line:match("%(%*([%w_]+)%)%([^%(%)]*%)") 626 | if key then 627 | local linet = line 628 | linet = linet:gsub("typedef ","") 629 | linet = linet:gsub("%(%*("..key..")%)","(*)") 630 | self.typedefs_dict[key] = linet 631 | else 632 | print("not found function typedef") 633 | print(key,value,line) 634 | end 635 | end 636 | end 637 | end 638 | local txt = table.concat(cdefs,"\n") 639 | itemsarr,items = parseItems(txt) 640 | self.itemsarr , self.items = itemsarr,items 641 | end 642 | function par:printItems() 643 | printItems(items) 644 | end 645 | par.parseFunction = parseFunction 646 | 647 | function par:parseFunctions() 648 | for i,it in ipairs(itemsarr) do 649 | if it.re_name == "function_re" or it.re_name == "functionD_re" then 650 | self:parseFunction("",it.item) 651 | elseif it.re_name == "namespace_re" then 652 | local nsp = it.item:match("%b{}"):sub(2,-2) 653 | local namespace = it.item:match("namespace%s+(%S+)") 654 | local nspparr,itemsnsp = parseItems(nsp) 655 | for insp,itnsp in ipairs(nspparr) do 656 | if itnsp.re_name == "function_re" or itnsp.re_name == "functionD_re" then 657 | self:parseFunction("",itnsp.item,namespace) 658 | end 659 | end 660 | elseif it.re_name == "struct_re" then 661 | local nsp = it.item:match("%b{}"):sub(2,-2) 662 | local stname = it.item:match("struct%s+(%S+)") 663 | local nspparr,itemsnsp = parseItems(nsp) 664 | for insp,itnsp in ipairs(nspparr) do 665 | if itnsp.re_name == "function_re" or itnsp.re_name == "functionD_re" then 666 | self:parseFunction(stname,itnsp.item) 667 | elseif itnsp.re_name == "struct_re" then 668 | --get embeded_structs 669 | local embededst = itnsp.item:match("struct%s+(%S+)") 670 | self.embeded_structs[embededst] = stname.."::"..embededst 671 | local nsp2 = strip_end(itnsp.item:match("%b{}"):sub(2,-2)) 672 | local itemsemarr,itemsem = parseItems(nsp2) 673 | assert(not itemsem.struct_re,"two level embed struct") 674 | for iemb,itemb in ipairs(itemsemarr) do 675 | if itemb.re_name == "function_re" or itemb.re_name == "functionD_re" then 676 | self:parseFunction(embededst,itemb.item) 677 | end 678 | end 679 | end 680 | end 681 | end 682 | end 683 | --require"anima" 684 | --prtable(self.defsT) 685 | end 686 | function par:clean_struct(stru) 687 | local outtab = {} 688 | local iner = strip_end(stru:match("%b{}"):sub(2,-2)) 689 | local stname = stru:match("struct%s*(%S+)%s*%b{}") 690 | if not stname then 691 | print(stru) 692 | error"could not get stname" 693 | end 694 | --initial 695 | table.insert(outtab,stru:match("(.-)%b{}")) 696 | table.insert(outtab,"{") 697 | local itlist,itemsin = parseItems(iner) 698 | if #itlist == 0 then return "" end --here we avoid empty structs 699 | for j,it in ipairs(itlist) do 700 | if it.re_name == "vardef_re" or it.re_name == "functype_re" or it.re_name == "union_re" then 701 | local it2 = it.item --:gsub("<([%w_]+)>","_%1") --templates 702 | --local ttype,template = it.item:match("([^%s,%(%)]+)%s*<(.+)>") 703 | local ttype,template = it.item:match"([^%s,%(%)]+)%s*<(.+)>" 704 | if template then 705 | local te = template:gsub("%s","_") 706 | te = te:gsub("%*","Ptr") 707 | self.templates[ttype] = self.templates[ttype] or {} 708 | self.templates[ttype][template] = te 709 | it2 = it2:gsub("<([%w_%*%s]+)>","_"..te) 710 | end 711 | --clean mutable 712 | it2 = it2:gsub("mutable","") 713 | --skip static variables 714 | if not (it.re_name == "vardef_re" and it2:match"static") then 715 | table.insert(outtab,it2) 716 | end 717 | elseif it.re_name == "struct_re" then 718 | table.insert(self.inerstructs,it) 719 | end 720 | end 721 | --final 722 | table.insert(outtab,"\n};") 723 | return table.concat(outtab,""),stname,outtab 724 | end 725 | function par:gen_structs_and_enums() 726 | local outtab = {} 727 | local outtabpre = {} 728 | local typedefs_table = {} 729 | 730 | --first typedefs 731 | for i,it in ipairs(itemsarr) do 732 | if it.re_name == "typedef_re" or it.re_name == "functypedef_re" or it.re_name == "vardef_re" then 733 | table.insert(outtabpre,it.item) 734 | -- add typedef after struct name 735 | if it.re_name == "vardef_re" and it.item:match"struct" then 736 | local stname = it.item:match("struct%s*(%S+)%s*;") 737 | table.insert(typedefs_table,"typedef struct "..stname.." "..stname..";\n") 738 | self.typedefs_dict[stname]="struct "..stname 739 | end 740 | end 741 | end 742 | --then structs and enums 743 | for i,it in ipairs(itemsarr) do 744 | if it.re_name == "enum_re" then 745 | table.insert(outtab,it.item) 746 | elseif it.re_name == "struct_re" then 747 | local cleanst,structname = self:clean_struct(it.item) 748 | if structname then 749 | table.insert(outtab,cleanst) 750 | table.insert(typedefs_table,"typedef struct "..structname.." "..structname..";\n") 751 | self.typedefs_dict[structname]="struct "..structname 752 | end 753 | end 754 | end 755 | --inner_structs 756 | for i,it in ipairs(self.inerstructs) do 757 | local cleanst,structname = self:clean_struct(it.item) 758 | if structname then 759 | table.insert(outtab,cleanst) 760 | table.insert(typedefs_table,"typedef struct "..structname.." "..structname..";\n") 761 | self.typedefs_dict[structname]="struct "..structname 762 | end 763 | end 764 | local uniques = {} 765 | for i,l in ipairs(typedefs_table) do 766 | if not uniques[l] then 767 | uniques[l] = true 768 | table.insert(outtabpre,1,l) 769 | end 770 | end 771 | --check arg detection failure if no name in function declaration 772 | check_arg_detection(self.defsT,self.typedefs_dict) 773 | return table.concat(outtabpre,""),table.concat(outtab,"") 774 | end 775 | ----------- 776 | function par:parse_struct_line(line,outtab) 777 | local functype_re = "^%s*[%w%s%*]+%(%*[%w_]+%)%([^%(%)]*%)" 778 | local functype_reex = "^(%s*[%w%s%*]+%(%*)([%w_]+)(%)%([^%(%)]*%))" 779 | line = clean_spaces(line) 780 | if line:match(functype_re) then 781 | local t1,name,t2 = line:match(functype_reex) 782 | table.insert(outtab,{type=t1..t2,name=name}) 783 | else 784 | --split type name1,name2; in several lines 785 | local typen,rest = line:match("%s*([^,]+)%s(%S+[,;])") 786 | --local template_type = typen:match("/%*<(.+)>%*/") 787 | --if template_type then typen = typen:match("(.+)/%*") end 788 | local template_type 789 | for k,v in pairs(self.templates) do 790 | template_type = typen:match(k.."_(.+)") 791 | if template_type then break end 792 | end 793 | if template_type then 794 | template_type = template_type:gsub("_"," ") 795 | template_type = template_type:gsub("Ptr","%*") 796 | end 797 | for name in rest:gmatch("([^%s,;]+)%s?[,;]") do 798 | table.insert(outtab,{type=typen,template_type=template_type,name=name}) 799 | end 800 | end 801 | end 802 | function par:gen_structs_and_enums_table() 803 | local outtab = {enums={},structs={}} 804 | local outtabpre = {} 805 | local typedefs_table = {} 806 | 807 | self.inerstructs = {} 808 | --then structs and enums 809 | for i,it in ipairs(itemsarr) do 810 | if it.re_name == "enum_re" then 811 | local enumname = it.item:match"^%s*enum%s+([^%s;{}]+)" 812 | outtab.enums[enumname] = {} 813 | local inner = strip_end(it.item:match("%b{}"):sub(2,-2)) 814 | local enumarr = str_split(inner,",") 815 | for j,line in ipairs(enumarr) do 816 | local name,value = line:match("%s*([%w_]+)%s*=%s*([^,]+)") 817 | if value then 818 | table.insert(outtab.enums[enumname],{name=name,value=value}) 819 | else --increment by one 820 | local name = line:match("%s*([^,]+)") 821 | local enum_table = outtab.enums[enumname] 822 | local value = enum_table[#enum_table] and (enum_table[#enum_table].value + 1) or 0 823 | table.insert(outtab.enums[enumname],{name=name,value=value}) 824 | end 825 | end 826 | elseif it.re_name == "struct_re" then 827 | local cleanst,structname,strtab = self:clean_struct(it.item) 828 | if structname then --not empty struc 829 | outtab.structs[structname] = {} 830 | for j=3,#strtab-1 do 831 | self:parse_struct_line(strtab[j],outtab.structs[structname]) 832 | end 833 | end 834 | end 835 | end 836 | --inner_structs 837 | 838 | for i,it in ipairs(self.inerstructs) do 839 | local cleanst,structname,strtab = self:clean_struct(it.item) 840 | if structname then --not empty struc 841 | outtab.structs[structname] = {} 842 | for j=3,#strtab-1 do 843 | self:parse_struct_line(strtab[j],outtab.structs[structname]) 844 | end 845 | end 846 | end 847 | --[[ 848 | local uniques = {} 849 | for i,l in ipairs(typedefs_table) do 850 | if not uniques[l] then 851 | uniques[l] = true 852 | table.insert(outtabpre,1,l) 853 | end 854 | end 855 | --]] 856 | --calcule size of name[16+1] [xxx_COUNT] 857 | local allenums = {} 858 | --first calc_value in enums 859 | for enumname,enum in pairs(outtab.enums) do 860 | for i,t in ipairs(enum) do 861 | local val = tonumber(t.value) 862 | if val then 863 | t.calc_value = val 864 | elseif t.value:match"<<" then 865 | local v1,v2 = t.value:match("(%d+)%s*<<%s*(%d+)") 866 | t.calc_value = bit.lshift(v1,v2) 867 | elseif t.value:match"|" then --or several enums 868 | local ens = t.value 869 | ens = strsplit(ens,"|") 870 | for i,v in ipairs(ens) do ens[i] = allenums[clean_spaces(v)] end 871 | t.calc_value = bit.bor(unpack(ens)) 872 | elseif allenums[t.value] then 873 | t.calc_value = allenums[t.value] 874 | else 875 | print("Error unknown value in enums",t.value) 876 | end 877 | assert(t.calc_value) 878 | allenums[t.name] = t.calc_value 879 | end 880 | end 881 | --then calcsize in struct members 882 | for stname,struct in pairs(outtab.structs) do 883 | for i,t in ipairs(struct) do 884 | local val = t.name:match"%[([^%[%]]+)%]" 885 | if val then 886 | if tonumber(val) then 887 | t.size = tonumber(val) 888 | elseif allenums[val] then 889 | t.size = allenums[val] 890 | elseif val:match"%+" then 891 | local s1,s2 = val:match("(%d+)%s*%+%s*(%d+)") 892 | t.size = s1+s2 893 | else 894 | print("Error size is",val) 895 | end 896 | assert(t.size) 897 | end 898 | end 899 | end 900 | return outtab 901 | end 902 | par.alltypes = {} 903 | local function get_types(v) 904 | for i,t in ipairs(v) do 905 | local signature = t.signature:sub(2,-2) -- without parenthesis 906 | for typec in signature:gsub("(%(.-%))", function(x) return x:gsub(",","\0") end):gmatch(".-([^,]+),?") do 907 | local key = typec:gsub("%z+", ",") 908 | par.alltypes[key] = true 909 | end 910 | end 911 | end 912 | function par:dump_alltypes() 913 | for k,v in pairs(self.alltypes) do print(k, typetoStr(k) ) end 914 | end 915 | function par:compute_overloads() 916 | local strt = {} 917 | local numoverloaded = 0 918 | self.alltypes = {} 919 | table.insert(strt,"----------------overloadings---------------------------") 920 | --require"anima.utils" 921 | for k,v in pairs(self.defsT) do 922 | get_types(v) 923 | if #v > 1 then 924 | numoverloaded = numoverloaded + #v 925 | --print(k,#v) 926 | table.insert(strt,string.format("%s\t%d",k,#v)) 927 | local typesc,post = name_overloadsAlgo(v) 928 | for i,t in ipairs(v) do 929 | --take overloaded name from manual table or algorythm 930 | t.ov_cimguiname = self.getCname_overload(t.stname,t.funcname,t.signature) or k..typetoStr(post[i]) 931 | table.insert(strt,string.format("%d\t%s\t%s %s",i,t.ret,t.ov_cimguiname,t.signature)) 932 | --prtable(typesc[i]) 933 | end 934 | --check not two names are equal (produced by bad cimguiname_overload) 935 | for i=1,#v-1 do 936 | for j=i+1,#v-1 do 937 | if v[i].ov_cimguiname == v[j].ov_cimguiname then 938 | local t,tj = v[i],v[j] 939 | print("Error caused by Bad overloading "..t.ov_cimguiname.." of function ",t.funcname,t.signature,"conflicts with ",tj.funcname,tj.signature) 940 | error("Bad overloading:"..t.ov_cimguiname) 941 | end 942 | end 943 | end 944 | else 945 | v[1].ov_cimguiname = v[1].cimguiname 946 | end 947 | end 948 | --print(numoverloaded, "overloaded") 949 | table.insert(strt,string.format("%d overloaded",numoverloaded)) 950 | AdjustArguments(self) 951 | ADDnonUDT(self) 952 | ADDdestructors(self) 953 | self.overloadstxt = table.concat(strt,"\n") 954 | end 955 | return par 956 | end 957 | 958 | ------serializeTable("anyname",table) gives a string that recreates the table with dofile(generated_string) 959 | local function serializeTable(name, value, saved) 960 | 961 | local function basicSerialize (o) 962 | if type(o) == "number" or type(o)=="boolean" then 963 | return tostring(o) 964 | elseif type(o) == "string" then 965 | return string.format("%q", o) 966 | else 967 | return "nil" 968 | end 969 | end 970 | 971 | local string_table = {} 972 | if not saved then 973 | table.insert(string_table, "local "..name.." = ") 974 | else 975 | table.insert(string_table, name.." = ") 976 | end 977 | 978 | saved = saved or {} -- initial value 979 | 980 | if type(value) == "number" or type(value) == "string" or type(value)=="boolean" then 981 | table.insert(string_table,basicSerialize(value).."\n") 982 | elseif type(value) == "table" then 983 | if saved[value] then -- value already saved? 984 | table.insert(string_table,saved[value].."\n") 985 | else 986 | saved[value] = name -- save name for next time 987 | table.insert(string_table, "{}\n") 988 | ---[[ 989 | local ordered_keys = {} 990 | for k,v in pairs(value) do 991 | table.insert(ordered_keys,k) 992 | end 993 | local function sorter(a,b) 994 | if type(a)==type(b) then 995 | return a