I copied code from: https://github.com/Youka/LuaWAV/blob/master/wav.lua https://love2d.org/forums/viewtopic.php?t=86173
5UCLBIFY4LTDA2EGR2SNK4Z7YPJ5IC3CSBYZ6ZYQQ7KYCRLSIZ4QC WIKPYMSPVUBU4F6NAUAM7WD7RMXCYZGWZ2Y2ZRVVQLYPYO65SQ3AC R5QXEHUIZLELJGGCZAE7ATNS3CLRJ7JFRENMGH4XXH24C5WABZDQC SDRXK4X5R6KBAFZTFWKTC7375HVVVPSTCDJVAYWSNUSHSKD242GQC 36Z442IVPXHZ7D2QI26YLN3TDDEMDRQ2GKBYQAD6NUHQZVCCY4VAC OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC LRDM35CEK3OHXOTB7TEFJRL7P6PQWO5ZG3F2BVA7DIDFHBPJQ7KAC --[[Library for simple audio reading, writing and analysing.Copyright © 2014, Christoph "Youka" SpanknebelPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.]]wav = {--[[Reads or writes audio file in WAVE format with PCM integer samples.Function 'create_context' requires 2 arguments: a filename and a mode, which can be "r" (read) or "w" (write).A call returns one table with methods depending on the used mode.On reading, following methods are callable:- get_filename()- get_mode()- get_file_size()- get_channels_number()- get_sample_rate()- get_byte_rate()- get_block_align()- get_bits_per_sample()- get_samples_per_channel()- get_sample_from_ms(ms)- get_ms_from_sample(sample)- get_min_max_amplitude()- get_position()- set_position(pos)- get_samples_interlaced(n)- get_samples(n)On writing, following methods are callable:- get_filename()- get_mode()- init(channels_number, sample_rate, bits_per_sample)- write_samples_interlaced(samples)- finish()(WAVE format: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/)]]create_context = function(filename, mode)-- Check function parametersif type(filename) ~= "string" or not (mode == "r" or mode == "w") thenerror("invalid function parameters, expected filename and mode \"r\" or \"w\"", 2)end-- Audio file handlelocal file = io.open(filename, mode == "r" and "rb" or "wb")if not file thenerror(string.format("couldn't open file %q", filename), 2)end-- Byte-string(unsigend integer,little endian)<->Lua-number converterslocal function bton(s)local bytes = {s:byte(1,#s)}local n, bytes_n = 0, #bytesfor i = 0, bytes_n-1 don = n + bytes[1+i] * 2^(i*8)endreturn nendlocal unpack = table.unpack or unpack -- Lua 5.1 or 5.2 table unpackerlocal function ntob(n, len)local n, bytes = math.max(math.floor(n), 0), {}for i=1, len dobytes[i] = n % 256n = math.floor(n / 256)endreturn string.char(unpack(bytes))end-- Check for integerlocal function isint(n)return type(n) == "number" and n == math.floor(n)end-- Initialize read processif mode == "r" then-- Audio meta informationslocal file_size, channels_number, sample_rate, byte_rate, block_align, bits_per_sample, samples_per_channel-- Audio samples file arealocal data_begin, data_end-- Read file typeif file:read(4) ~= "RIFF" thenerror("not a RIFF file", 2)endfile_size = file:read(4)if not file_size thenerror("file header incomplete (file size)")endfile_size = bton(file_size) + 8if file:read(4) ~= "WAVE" thenerror("not a WAVE file", 2)end-- Read file chunkslocal chunk_id, chunk_sizewhile true do-- Read chunk headerchunk_id, chunk_size = file:read(4), file:read(4)if not chunk_size thenbreakendchunk_size = bton(chunk_size)-- Identify chunk typeif chunk_id == "fmt " then-- Read format informationslocal bytes = file:read(2)if not bytes or bton(bytes) ~= 1 thenerror("data must be in PCM format", 2)endbytes = file:read(2)if not bytes thenerror("channels number not found", 2)endchannels_number = bton(bytes)bytes = file:read(4)if not bytes thenerror("sample rate not found", 2)endsample_rate = bton(bytes)bytes = file:read(4)if not bytes thenerror("byte rate not found", 2)endbyte_rate = bton(bytes)bytes = file:read(2)if not bytes thenerror("block align not found", 2)endblock_align = bton(bytes)bytes = file:read(2)if not bytes thenerror("bits per sample not found")endbits_per_sample = bton(bytes)if bits_per_sample ~= 8 and bits_per_sample ~= 16 and bits_per_sample ~= 24 and bits_per_sample ~= 32 thenerror("bits per sample must be 8, 16, 24 or 32", 2)endfile:seek("cur", chunk_size-16)elseif chunk_id == "data" then-- Read samplesif not block_align thenerror("format informations must be defined before sample data", 2)endsamples_per_channel = chunk_size / block_aligndata_begin = file:seek()data_end = data_begin + chunk_sizebreak -- Stop here for later readingelse-- Skip chunkfile:seek("cur", chunk_size)endend-- Enough informations available?if not bits_per_sample thenerror("no format informations found", 2)end-- Return audio handlerlocal objobj = {get_filename = function()return filenameend,get_mode = function()return modeend,get_file_size = function()return file_sizeend,get_channels_number = function()return channels_numberend,get_sample_rate = function()return sample_rateend,get_byte_rate = function()return byte_rateend,get_block_align = function()return block_alignend,get_bits_per_sample = function()return bits_per_sampleend,get_samples_per_channel = function()return samples_per_channelend,get_sample_from_ms = function(ms)if not isint(ms) or ms < 0 thenerror("positive integer expected", 2)endreturn ms * 0.001 * sample_rateend,get_ms_from_sample = function(sample)if not isint(sample) or sample < 0 thenerror("positive integer expected", 2)endreturn sample / sample_rate * 1000end,get_min_max_amplitude = function()local half_level = 2^bits_per_sample / 2return -half_level, half_level - 1end,get_position = function()if not data_begin thenerror("no samples available", 2)endreturn (file:seek() - data_begin) / block_alignend,set_position = function(pos)if not isint(pos) or pos < 0 thenerror("positive integer expected", 2)elseif not data_begin thenerror("no samples available", 2)elseif data_begin + pos * block_align > data_end thenerror("tried to set position behind data end", 2)endfile:seek("set", data_begin + pos * block_align)end,get_samples_interlaced = function(n)if not isint(n) or n <= 0 thenerror("positive integer greater zero expected", 2)elseif not data_begin thenerror("no samples available", 2)elseif file:seek() + n * block_align > data_end thenerror("tried to read over data end", 2)endlocal bytes, sample, output = file:read(n * block_align), nil, {n = 0}local bytes_n = #bytesif bits_per_sample == 8 thenfor i=1, bytes_n, 1 dosample = bton(bytes:sub(i,i))output.n = output.n + 1output[output.n] = sample > 127 and sample - 256 or sampleendelseif bits_per_sample == 16 thenfor i=1, bytes_n, 2 dosample = bton(bytes:sub(i,i+1))output.n = output.n + 1output[output.n] = sample > 32767 and sample - 65536 or sampleendelseif bits_per_sample == 24 thenfor i=1, bytes_n, 3 dosample = bton(bytes:sub(i,i+2))output.n = output.n + 1output[output.n] = sample > 8388607 and sample - 16777216 or sampleendelse -- if bits_per_sample == 32 thenfor i=1, bytes_n, 4 dosample = bton(bytes:sub(i,i+3))output.n = output.n + 1output[output.n] = sample > 2147483647 and sample - 4294967296 or sampleendendreturn outputend,get_samples = function(n)local success, samples = pcall(obj.get_samples_interlaced, n)if not success thenerror(samples, 2)endlocal output, channel_samples = {n = channels_number}for c=1, output.n dochannel_samples = {n = samples.n / channels_number}for s=1, channel_samples.n dochannel_samples[s] = samples[c + (s-1) * channels_number]endoutput[c] = channel_samplesendreturn outputend}return obj-- Initialize write processelse-- Audio meta informationslocal channels_number_private, bytes_per_sample-- Return audio handlerreturn {get_filename = function()return filenameend,get_mode = function()return modeend,init = function(channels_number, sample_rate, bits_per_sample)-- Check function parametersif not isint(channels_number) or channels_number < 1 ornot isint(sample_rate) or sample_rate < 2 ornot (bits_per_sample == 8 or bits_per_sample == 16 or bits_per_sample == 24 or bits_per_sample == 32) thenerror("valid channels number, sample rate and bits per sample expected", 2)-- Already finished?elseif not file thenerror("already finished", 2)-- Already initialized?elseif file:seek() > 0 thenerror("already initialized", 2)end-- Write file typefile:write("RIFF????WAVE") -- file size to insert later-- Write format chunkfile:write("fmt ",ntob(16, 4),ntob(1, 2),ntob(channels_number, 2),ntob(sample_rate, 4),ntob(sample_rate * channels_number * (bits_per_sample / 8), 4),ntob(channels_number * (bits_per_sample / 8), 2),ntob(bits_per_sample, 2))-- Write data chunk (so far)file:write("data????") -- data size to insert later-- Set format memorychannels_number_private, bytes_per_sample = channels_number, bits_per_sample / 8end,write_samples_interlaced = function(samples)-- Check function parametersif type(samples) ~= "table" thenerror("samples table expected", 2)endlocal samples_n = #samplesif samples_n == 0 or samples_n % channels_number_private ~= 0 thenerror("valid number of samples expected (multiple of channels)", 2)-- Already finished?elseif not file thenerror("already finished", 2)-- Already initialized?elseif file:seek() == 0 thenerror("initialize before writing samples", 2)end-- All samples are numbers?for i=1, samples_n doif type(samples[i]) ~= "number" thenerror("samples have to be numbers", 2)endend-- Write samples to filelocal sampleif bytes_per_sample == 1 thenfor i=1, samples_n dosample = samples[i]file:write(ntob(sample < 0 and sample + 256 or sample, 1))endelseif bytes_per_sample == 2 thenfor i=1, samples_n dosample = samples[i]file:write(ntob(sample < 0 and sample + 65536 or sample, 2))endelseif bytes_per_sample == 3 thenfor i=1, samples_n dosample = samples[i]file:write(ntob(sample < 0 and sample + 16777216 or sample, 3))endelse -- if bytes_per_sample == 4 thenfor i=1, samples_n dosample = samples[i]file:write(ntob(sample < 0 and sample + 4294967296 or sample, 4))endendend,finish = function()-- Already finished?if not file thenerror("already finished", 2)-- Already initialized?elseif file:seek() == 0 thenerror("initialize before finishing", 2)end-- Get file sizelocal file_size = file:seek()-- Save file sizefile:seek("set", 4)file:write(ntob(file_size - 8, 4))-- Save data sizefile:seek("set", 40)file:write(ntob(file_size - 44, 4))-- Finalize file for secure readingfile:close()file = nilend}endend,--[[Analyses frequencies of audio samples.Function 'create_frequency_analyzer' requires 2 arguments: a table with audio samples and the relating sample rate.A call returns one table with following methods:- get_frequencies()- get_frequency_weight(freq)- get_frequency_range_weight(freq_min, freq_max)(FFT: http://www.relisoft.com/science/physics/fft.html)]]create_frequency_analyzer = function(samples, sample_rate)-- Check function parametersif type(samples) ~= "table" ortype(sample_rate) ~= "number" or sample_rate ~= math.floor(sample_rate) or sample_rate < 2 thenerror("samples table and sample rate expected", 2)endlocal samples_n = #samplesif samples_n ~= math.ceil_pow2(samples_n) thenerror("table size has to be a power of two", 2)endfor _, sample in ipairs(samples) doif type(sample) ~= "number" thenerror("table has only to contain numbers", 2)elseif sample > 1 or sample < -1 thenerror("numbers should be normalized / limited to -1 until 1", 2)endend-- Complex numberslocal complex_tdolocal complex = {}local function tocomplex(a, b)if getmetatable(b) ~= complex then return a, {r = b, i = 0}elseif getmetatable(a) ~= complex then return {r = a, i = 0}, belse return a, b endendcomplex.__add = function(a, b)local c1, c2 = tocomplex(a, b)return setmetatable({r = c1.r + c2.r, i = c1.i + c2.i}, complex)endcomplex.__sub = function(a, b)local c1, c2 = tocomplex(a, b)return setmetatable({r = c1.r - c2.r, i = c1.i - c2.i}, complex)endcomplex.__mul = function(a, b)local c1, c2 = tocomplex(a, b)return setmetatable({r = c1.r * c2.r - c1.i * c2.i, i = c1.r * c2.i + c1.i * c2.r}, complex)endcomplex.__index = complexcomplex_t = function(r, i)return setmetatable({r = r, i = i}, complex)endendlocal function polar(theta)return complex_t(math.cos(theta), math.sin(theta))endlocal function magnitude(c)return math.sqrt(c.r^2 + c.i^2)end-- Fast Fourier Transformlocal function fft(x)-- Check recursion breaklocal N = x.nif N > 1 then-- Dividelocal even, odd = {n = 0}, {n = 0}for i=1, N, 2 doeven.n = even.n + 1even[even.n] = x[i]endfor i=2, N, 2 doodd.n = odd.n + 1odd[odd.n] = x[i]end-- Conquerfft(even)fft(odd)--Combinelocal tfor k = 1, N/2 dot = polar(-2 * math.pi * (k-1) / N) * odd[k]x[k] = even[k] + tx[k+N/2] = even[k] - tendendend-- Numbers to complex numberslocal data = {n = samples_n}for i = 1, data.n dodata[i] = complex_t(samples[i], 0)end-- Process FFTfft(data)-- Complex numbers to numbersfor i = 1, data.n dodata[i] = magnitude(data[i])end-- Calculate ordered frequencieslocal frequencies, frequency_sum, sample_rate_half = {n = data.n / 2}, 0, sample_rate / 2for i=1, frequencies.n dofrequency_sum = frequency_sum + data[i]endif frequency_sum > 0 thenfor i=1, frequencies.n dofrequencies[i] = {freq = (i-1) / (frequencies.n-1) * sample_rate_half, weight = data[i] / frequency_sum}endelsefrequencies[1] = {freq = 0, weight = 1}for i=2, frequencies.n dofrequencies[i] = {freq = (i-1) / (frequencies.n-1) * sample_rate_half, weight = 0}endend-- Return frequencies getterreturn {get_frequencies = function()local out = {n = frequencies.n}for i=1, frequencies.n doout[i] = {freq = frequencies[i].freq, weight = frequencies[i].weight}endreturn outend,get_frequency_weight = function(freq)if type(freq) ~= "number" or freq < 0 or freq > sample_rate_half thenerror("valid frequency expected", 2)endfor i, frequency in ipairs(frequencies) doif frequency.freq == freq thenreturn frequency.weightelseif frequency.freq > freq thenlocal frequency_last = frequencies[i-1]return (freq - frequency_last.freq) / (frequency.freq - frequency_last.freq) * (frequency.weight - frequency_last.weight) + frequency_last.weightendendend,get_frequency_range_weight = function(freq_min, freq_max)if type(freq_min) ~= "number" or freq_min < 0 or freq_min > sample_rate_half ortype(freq_max) ~= "number" or freq_max < 0 or freq_max > sample_rate_half orfreq_min > freq_max thenerror("valid frequencies expected", 2)endlocal weight_sum = 0for _, frequency in ipairs(frequencies) doif frequency.freq >= freq_min and frequency.freq <= freq_max thenweight_sum = weight_sum + frequency.weightendendreturn weight_sumend}end}--[[Rounds up number to power of 2.]]function math.ceil_pow2(x)if type(x) ~= "number" thenerror("number expected", 2)endlocal p = 2while p < x dop = p * 2endreturn pend--[[Rounds down number to power of 2.]]function math.floor_pow2(x)if type(x) ~= "number" thenerror("number expected", 2)endlocal y = math.ceil_pow2(x)return x == y and x or y / 2end--[[Rounds number nearest to power of 2.]]function math.round_pow2(x)if type(x) ~= "number" thenerror("number expected", 2)endlocal min, max = math.floor_pow2(x), math.ceil_pow2(x)return (x - min) / (max-min) < 0.5 and min or maxend--[[Converts samples into an ASS (Advanced Substation Alpha) subtitle shape code.]]function audio_to_ass(samples, wave_width, wave_height_scale, wave_thickness)-- Check function parametersif type(samples) ~= "table" or not samples[1] ortype(wave_width) ~= "number" or wave_width <= 0 ortype(wave_height_scale) ~= "number" ortype(wave_thickness) ~= "number" or wave_thickness <= 0 thenerror("samples table, positive wave width, height scale and thickness expected", 2)endfor _, sample in ipairs(samples) doif type(sample) ~= "number" thenerror("table has only to contain numbers", 2)endend-- Better fitting forms of known variables for most uselocal thick2, samples_n = wave_thickness / 2, #samples-- Build shapelocal shape = string.format("m 0 %d l", samples[1] * wave_height_scale - thick2)for i=2, samples_n doshape = string.format("%s %d %d", shape, (i-1) / (samples_n-1) * wave_width, samples[i] * wave_height_scale - thick2)endfor i=samples_n, 1, -1 doshape = string.format("%s %d %d", shape, (i-1) / (samples_n-1) * wave_width, samples[i] * wave_height_scale + thick2)endreturn shapeend
play = function(word_idx)if Words[word_idx].recording thenPlaying_source = love.audio.newSource(Words[word_idx].recording)elseif love.filesystem.exists(Words[word_idx].contents..'.wav') thenWords[word_idx].recording = love.sound.newSoundData(Words[word_idx].contents..'.wav')Playing_source = love.audio.newSource(Words[word_idx].recording)elsePlaying_source = love.audio.newSource('sample.wav', 'static')endPlaying_source:play()Recording_device = nilend
{"draw_new_word_button":117,"Font_height":8,"learn_mouse_press":57,"Line_height":9,"learn_mouse_release":58,"Recording_device":132,"learn_keychord_press":59,"learn_text_input":60,"learn_key_release":61,"Recording_word":138,"on.mouse_press":51,"draw_record_button":140,"on.mouse_release":52,"draw_play_button":141,"fw_parent":173,"on.key_release":55,"on.text_input":54,"Cursor_word":24,"play":174,"on_new_word_button":118,"new_word":64,"on_word":80,"to_word_idx":94,"record":172,"draw_recording_button":133,"draw_playing_button":130,"draw_teach_word":68,"on":1,"save_wav":171,"on.keychord_press":53,"on_record":95,"draw_teach_screen":110,"teach_mouse_release":46,"Mode":2,"teach_keychord_press":47,"teach_mouse_press":88,"teach_key_release":56,"on_play":96,"teach_text_input":49,"fw_app":"spell-cards","on.draw":5,"draw_learn_screen":4,"Words":12,"Playing_source":126,"on.initialize":7}
play = function(word_idx)if Words[word_idx].recording thenPlaying_source = love.audio.newSource(Words[word_idx].recording)elseif love.filesystem.exists(Words[word_idx].contents..'.wav') thenWords[word_idx].recording = love.sound.newSoundData(Words[word_idx].contents..'.sounddata')Playing_source = love.audio.newSource(Words[word_idx].recording)elsePlaying_source = love.audio.newSource('sample.wav', 'static')endPlaying_source:play()Recording_device = nilend
{"draw_record_button":140,"draw_play_button":141,"on.mouse_press":51,"on.mouse_release":52,"fw_parent":172,"Playing_source":126,"fw_app":"spell-cards","on.key_release":55,"on_new_word_button":118,"on.text_input":54,"Cursor_word":24,"Recording_device":132,"on_word":80,"new_word":64,"Recording_word":138,"record":172,"on":1,"on_play":96,"on_record":95,"draw_teach_word":68,"draw_learn_screen":4,"to_word_idx":94,"teach_mouse_press":88,"play":173,"teach_mouse_release":46,"draw_recording_button":133,"teach_keychord_press":47,"save_wav":171,"teach_key_release":56,"learn_keychord_press":59,"teach_text_input":49,"draw_new_word_button":117,"on.draw":5,"learn_mouse_press":57,"Words":12,"learn_mouse_release":58,"on.initialize":7,"draw_playing_button":130,"Font_height":8,"learn_text_input":60,"Line_height":9,"learn_key_release":61,"Mode":2,"draw_teach_screen":110,"on.keychord_press":53}
record = function(word_idx)if Recording_device thenWords[word_idx].recording = Recording_device:getData()Recording_device:stop()if Words[word_idx].recording thensave_wav(Words[word_idx].contents..'.wav', Words[word_idx].recording)endRecording_device = nilRecording_word = nilreturnendlocal devices = love.audio.getRecordingDevices()if #devices == 0 then return endRecording_device = devices[1]Recording_device:start()Recording_word = Words[word_idx]Playing_source = nilend
{"draw_record_button":140,"draw_play_button":141,"on.mouse_press":51,"on.mouse_release":52,"fw_parent":171,"Playing_source":126,"fw_app":"spell-cards","on.key_release":55,"on_new_word_button":118,"on.text_input":54,"Cursor_word":24,"Recording_device":132,"on_word":80,"new_word":64,"Recording_word":138,"record":172,"on":1,"on_play":96,"on_record":95,"draw_teach_word":68,"draw_learn_screen":4,"to_word_idx":94,"teach_mouse_press":88,"play":169,"teach_mouse_release":46,"draw_recording_button":133,"teach_keychord_press":47,"save_wav":171,"teach_key_release":56,"learn_keychord_press":59,"teach_text_input":49,"draw_new_word_button":117,"on.draw":5,"learn_mouse_press":57,"Words":12,"learn_mouse_release":58,"on.initialize":7,"draw_playing_button":130,"Font_height":8,"learn_text_input":60,"Line_height":9,"learn_key_release":61,"Mode":2,"draw_teach_screen":110,"on.keychord_press":53}
save_wav = function(filename, sounddata)-- credit to Intas and zorg on the LÖVE forum <3-- https://love2d.org/forums/viewtopic.php?t=86173local samples = {}for i = 0, sounddata:getSampleCount() - 1 dolocal sample = sounddata:getSample(i)local nlocal to16bit = sample * 32767if (to16bit > 0) thenn = math.floor(math.min(to16bit, 32767))elsen = math.floor(math.max(to16bit, -32768))endtable.insert(samples, n)endlocal w = wav.create_context(filename, "w")w.init(sounddata:getChannelCount(), sounddata:getSampleRate(), sounddata:getBitDepth())w.write_samples_interlaced(samples)w.finish()end
{"draw_record_button":140,"draw_play_button":141,"on.mouse_press":51,"on.mouse_release":52,"fw_parent":170,"Playing_source":126,"fw_app":"spell-cards","on.key_release":55,"on_new_word_button":118,"on.text_input":54,"Cursor_word":24,"Recording_device":132,"on_word":80,"new_word":64,"Recording_word":138,"record":167,"on":1,"on_play":96,"on_record":95,"draw_teach_word":68,"draw_learn_screen":4,"to_word_idx":94,"teach_mouse_press":88,"play":169,"teach_mouse_release":46,"draw_recording_button":133,"teach_keychord_press":47,"save_wav":171,"teach_key_release":56,"learn_keychord_press":59,"teach_text_input":49,"draw_new_word_button":117,"on.draw":5,"learn_mouse_press":57,"Words":12,"learn_mouse_release":58,"on.initialize":7,"draw_playing_button":130,"Font_height":8,"learn_text_input":60,"Line_height":9,"learn_key_release":61,"Mode":2,"draw_teach_screen":110,"on.keychord_press":53}
save_wav = function(filename, sounddata)-- credit to Intas and zorg on the LÖVE forum <3-- https://love2d.org/forums/viewtopic.php?t=86173local samples = {}for i = 0, sounddata:getSampleCount() - 1 dolocal sample = sounddata:getSample(i)local nlocal to16bit = sample * 32767if (to16bit > 0) thenn = math.floor(math.min(to16bit, 32767))elsen = math.floor(math.max(to16bit, -32768))endtable.insert(samples, n)endlocal w = wav.create_context("test.wav", "w")w.init(sounddata:getChannelCount(), sounddata:getSampleRate(), sounddata:getBitDepth())w.write_samples_interlaced(samples)w.finish()end
{"draw_record_button":140,"draw_play_button":141,"on.mouse_press":51,"on.mouse_release":52,"fw_parent":169,"Playing_source":126,"fw_app":"spell-cards","on.key_release":55,"on_new_word_button":118,"on.text_input":54,"Cursor_word":24,"Recording_device":132,"on_word":80,"new_word":64,"Recording_word":138,"record":167,"on":1,"on_play":96,"on_record":95,"draw_teach_word":68,"draw_learn_screen":4,"to_word_idx":94,"teach_mouse_press":88,"play":169,"teach_mouse_release":46,"draw_recording_button":133,"teach_keychord_press":47,"save_wav":170,"teach_key_release":56,"learn_keychord_press":59,"teach_text_input":49,"draw_new_word_button":117,"on.draw":5,"learn_mouse_press":57,"Words":12,"learn_mouse_release":58,"on.initialize":7,"draw_playing_button":130,"Font_height":8,"learn_text_input":60,"Line_height":9,"learn_key_release":61,"Mode":2,"draw_teach_screen":110,"on.keychord_press":53}
play = function(word_idx)if Words[word_idx].recording thenPlaying_source = love.audio.newSource(Words[word_idx].recording)elseif love.filesystem.exists(Words[word_idx].contents..'.sounddata') thenWords[word_idx].recording = love.sound.newSoundData(Words[word_idx].contents..'.sounddata')Playing_source = love.audio.newSource(Words[word_idx].recording)elsePlaying_source = love.audio.newSource('test.wav', 'static')endPlaying_source:play()Recording_device = nilend
{"record":167,"new_word":64,"on":1,"save_wav":168,"on_record":95,"draw_teach_word":68,"teach_mouse_press":88,"teach_mouse_release":46,"draw_new_word_button":117,"draw_teach_screen":110,"teach_keychord_press":47,"draw_recording_button":133,"fw_parent":168,"on.draw":5,"fw_app":"spell-cards","Words":12,"teach_key_release":56,"on.initialize":7,"on.keychord_press":53,"Font_height":8,"learn_mouse_press":57,"Line_height":9,"learn_mouse_release":58,"Mode":2,"learn_keychord_press":59,"Recording_device":132,"learn_text_input":60,"Playing_source":126,"learn_key_release":61,"Recording_word":138,"draw_playing_button":130,"teach_text_input":49,"on_play":96,"on.mouse_press":51,"draw_record_button":140,"on_new_word_button":118,"draw_play_button":141,"play":169,"on.mouse_release":52,"on.key_release":55,"on_word":80,"on.text_input":54,"Cursor_word":24,"to_word_idx":94,"draw_learn_screen":4}
save_wav = function(filename, sounddata)local channelCount = sounddata:getChannelCount()local sampleRate = sounddata:getSampleRate()local bitDepth = sounddata:getBitDepth()local samples = {}for i = 0, sounddata:getSampleCount() - 1 dolocal sample = sounddata:getSample(i)local nlocal to16bit = sample * 32767if (to16bit > 0) thenn = math.floor(math.min(to16bit, 32767))elsen = math.floor(math.max(to16bit, -32768))endtable.insert(samples, n)endlocal w = wav.create_context("test.wav", "w")w.init(channelCount, sampleRate, bitDepth)w.write_samples_interlaced(samples)w.finish()end
{"record":167,"new_word":64,"on":1,"save_wav":168,"on_record":95,"draw_teach_word":68,"teach_mouse_press":88,"teach_mouse_release":46,"draw_new_word_button":117,"draw_teach_screen":110,"teach_keychord_press":47,"draw_recording_button":133,"fw_parent":167,"on.draw":5,"fw_app":"spell-cards","Words":12,"teach_key_release":56,"on.initialize":7,"on.keychord_press":53,"Font_height":8,"learn_mouse_press":57,"Line_height":9,"learn_mouse_release":58,"Mode":2,"learn_keychord_press":59,"Recording_device":132,"learn_text_input":60,"Playing_source":126,"learn_key_release":61,"Recording_word":138,"draw_playing_button":130,"teach_text_input":49,"on_play":96,"on.mouse_press":51,"draw_record_button":140,"on_new_word_button":118,"draw_play_button":141,"play":165,"on.mouse_release":52,"on.key_release":55,"on_word":80,"on.text_input":54,"Cursor_word":24,"to_word_idx":94,"draw_learn_screen":4}
record = function(word_idx)if Recording_device thenWords[word_idx].recording = Recording_device:getData()Recording_device:stop()if Words[word_idx].recording thensave_wav(Words[word_idx].contents..'.sounddata', Words[word_idx].recording)endRecording_device = nilRecording_word = nilreturnendlocal devices = love.audio.getRecordingDevices()if #devices == 0 then return endRecording_device = devices[1]Recording_device:start()Recording_word = Words[word_idx]Playing_source = nilend
{"record":167,"new_word":64,"on":1,"save_wav":166,"on_record":95,"draw_teach_word":68,"teach_mouse_press":88,"teach_mouse_release":46,"draw_new_word_button":117,"draw_teach_screen":110,"teach_keychord_press":47,"draw_recording_button":133,"fw_parent":166,"on.draw":5,"fw_app":"spell-cards","Words":12,"teach_key_release":56,"on.initialize":7,"on.keychord_press":53,"Font_height":8,"learn_mouse_press":57,"Line_height":9,"learn_mouse_release":58,"Mode":2,"learn_keychord_press":59,"Recording_device":132,"learn_text_input":60,"Playing_source":126,"learn_key_release":61,"Recording_word":138,"draw_playing_button":130,"teach_text_input":49,"on_play":96,"on.mouse_press":51,"draw_record_button":140,"on_new_word_button":118,"draw_play_button":141,"play":165,"on.mouse_release":52,"on.key_release":55,"on_word":80,"on.text_input":54,"Cursor_word":24,"to_word_idx":94,"draw_learn_screen":4}
save_wav = function(filename, soundData)end
{"record":162,"new_word":64,"on":1,"save_wav":166,"on_record":95,"draw_teach_word":68,"teach_mouse_press":88,"teach_mouse_release":46,"draw_new_word_button":117,"draw_teach_screen":110,"teach_keychord_press":47,"draw_recording_button":133,"fw_parent":165,"on.draw":5,"fw_app":"spell-cards","Words":12,"teach_key_release":56,"on.initialize":7,"on.keychord_press":53,"Font_height":8,"learn_mouse_press":57,"Line_height":9,"learn_mouse_release":58,"Mode":2,"learn_keychord_press":59,"Recording_device":132,"learn_text_input":60,"Playing_source":126,"learn_key_release":61,"Recording_word":138,"draw_playing_button":130,"teach_text_input":49,"on_play":96,"on.mouse_press":51,"draw_record_button":140,"on_new_word_button":118,"draw_play_button":141,"play":165,"on.mouse_release":52,"on.key_release":55,"on_word":80,"on.text_input":54,"Cursor_word":24,"to_word_idx":94,"draw_learn_screen":4}