Page 1 of 1

The Wave (2.0)

Posted: Wed Sep 30, 2015 7:20 pm
by Nixola
Hi! I wrote a utility to save PCM .wav files some time ago, but it was kinda buggy, it only worked properly with 16-bit files and only saved SoundDatas. I tried to rewrite it and it seems it works better (also, it's now 0.9.2 compatible), but I'm not completely sure about that. Here's how it works:

Code: Select all

local wave = require "wave"
local sData = love.sound.newSoundData "Example.ogg"
wave.save{ filename = "Example.wav", sound = sData, overwrite = true, callback = function() love.graphics.setColor(255, 0, 0) end}
This is the simplest it gets. You require the lib, call wave.save giving it a SoundData, a filename, an optional callback which is called when the file is saved) and an optional "force" parameter (if true, The Wave will overwrite the file if it already exists). When saving SoundDatas, this is pretty much all you have to worry about. It automatically gets the parameters (bitrate, bit depth, channels number) from the SoundData and is fast enough you probably don't have to worry about a callback: if you don't like it being asynchronous, you can pass "false" (the value, not the string) as callback and it'll wait until the thread's done. The callback is only called if you call wave.update in your love.update though.

There is also a more complex way to use it:

Code: Select all

local t = {}

t.bitDepth = 16
t.channels = 1
t.sampleRate = 48000

local BIRATEPI = t.sampleRate/math.pi/2

local sine = function(s, v, a) return a*math.sin(s*v/BIRATEPI) end

for i = 1, t.sampleRate*15 do
    t[i] = sine(i, 220, 2)
end

local sin = wave.save{ sound = t, filename = "Sine.wav", callback = function() love.system.openURL(love.filesystem.getSaveDirectory()) end, overwrite = true, exceptionMode = "normalize" }

love.update = function(dt)
    wave.update(dt)
end

love.draw = function()
    love.graphics.print("This is not a fancy demo. If everything is red, the .ogg file was saved as a wav file.\nWhen the following's done, a folder will automagically be opened.", 0, 0)
    local p = sin:getPercent()
    if p then
        love.graphics.print(p .. "%", 0, 32)
    end
    local s = sin:getStatus()
    if s then
        love.graphics.print(s, 0, 48)
    end
    if sin:getError() then
        love.graphics.print(sin:getError(), 0, 64)
    end
end
wave.save also return a handy handler (hey! pun there! laugh!) that allows you to query Wave for the saving status (it can return "analyzing", "saving", "done" or "error"), how much it saved (only if you save a table though; it will be nil if you're saving a SoundData) and, if the thread errors, the error message. An error might be caused if you pass an invalid table or an invalid userdata (pretty much anything but SoundData). A table is only valid if it has an array part and that array only contains numbers, and if it has the fields bitDepth (which also has to be either 8 or 16), channel (which must be a positive number) and sampleRate (which must be a positive number). It can also error if the file exists and the 4th argument isn't true-ish, or if it can't write to the file.

wave.save can receive normal arguments (filename, soundData or table, callback, overwrite, exceptionMode) or a table {filename = string, sound = soundData or table, callback = func/false, overwrite = true, exceptionMode = string}.
The filename is, of course, the name/path the file will end up into in the save directory.
Sound is either a SoundData or the aforementioned table.
Callback is a function that will be called if the file is succesfully saved or if the thread encountered an error. It gets one argument, a boolean which is true if the thread's done and false if it errored. If the callback itself is false (not nil!), wave.save will behave synchronously and block everything till it's done. The function itself will return true if the file got saved correctly or nil, error if the thread encountered an error.
Overwrite is a boolean that tells Wave wether to overwrite a file, if it already exists.
ExceptionMode is the one single reason the API got changed a few hours after first release. It only makes sense if you're saving a table and it can have three values:
  • "error": if any single sample is greater than 1 or lesser than -1, the thread will halt and Wave won't even try to save. Default if unspecified.
  • "clip": if any sample is greater than 1 or lesser than -1, it becomes 1 or -1 respectively. Standard clipping.
  • "normalize": normalizes the whole table, dividing every sample by the absolute value of the biggest one (thus making it 1 or -1 and every other one smaller)
I'm sure I forgot something. If you've got any questions, feel free to ask! The .love is down here.

EDIT: A point has been brought up on IRC. Poll!
EDIT2: Poll's over. 0 results, but a good talk on IRC brought a solution. Thanks, z0rg and DesertRock!

Re: The Wave (2.0)

Posted: Sun Oct 04, 2015 1:49 am
by boobeebah13
lol syntax error in the .love file

Re: The Wave (2.0)

Posted: Mon Oct 05, 2015 2:06 pm
by Nixola
I should test things more often. Thanks! .love updated.