Page 1 of 1

[Solved] Is there any way to save sounddata to a file?

Posted: Sat Dec 29, 2018 10:13 pm
by Intas
Hello.

With the microphone recording thing you can get the sounddata using RecordingDevice:getData() and with that you can create a source using love.audio.newSource(). Now I'd like to know if is possible to store that sounddata into a file (.wav? .mp3?) so I can play anytime whatever the microphone have recorded.

Thanks!

Re: Is there any way to save sounddata to a file?

Posted: Sun Dec 30, 2018 1:31 am
by zorg
Hi and welcome to the forums!

You can write out the contents sample-by-sample using love.filesystem stuff, but löve doesn't have a way, as far as i know, to encode it to a format it can load; you could write out a wav header and then the data using the correct format, which would be the simplest format to do, in my opinion.

Re: Is there any way to save sounddata to a file?

Posted: Sun Dec 30, 2018 9:40 pm
by Intas
Hey zorg, thanks for replying!
zorg wrote: Sun Dec 30, 2018 1:31 am you could write out a wav header and then the data using the correct format, which would be the simplest format to do, in my opinion.
Could you point me in the right direction on how could I do this? Is it something I should do using ffi? I'm completely unfamiliar with it :?

Re: Is there any way to save sounddata to a file?

Posted: Mon Dec 31, 2018 3:02 pm
by zorg
Here's a site i found that's helpful, in that it shows how the (most used variation of the) format works: http://soundfile.sapp.org/doc/WaveFormat/

You don't need to use the FFI for this though, it can be implemented with just lua, tables, numbers and some string manipulation (to write out the correctly formatted numbers)

You can get the sampling rate, bit depth and channel count from the sounddata itself.

Re: Is there any way to save sounddata to a file?

Posted: Wed Jan 02, 2019 12:04 am
by Intas
Okay, so I found this which seems to be what I want. It's made in pure lua so I have to change lua's IO with love.filesystem.

Code: Select all

require("wav")
-- ...
local samples = {}
local sounddata = rd:getData()
rd:stop()
local source = love.audio.newSource(sounddata)
love.audio.play(source) -- Play whatever was recorded

local w = wav.create_context("audiotest.wav", "w")

local channelCount = sounddata:getChannelCount()
local sampleRate   = sounddata:getSampleRate()
local bitDepth     = sounddata:getBitDepth() -- bits per sample

w.init(channelCount, sampleRate, bitDepth)
w.write_samples_interlaced(samples) -- ???
w.finish()
The only problem is that I don't know what "samples" should contain? According to the test file the samples are calculated like this: math.sin(i % 44100 / 44099 * freq) * 32767
Where:
  • i is a number from 0 to 44100*3
  • 44100 is the sample rate,
  • 44099 is sample rate - 1,
  • freq is math.pi * 2 * 500
  • 32767 is the maximum value for a signed integer?
Those last two values are magic numbers for me because I have no idea what they mean :?

Guess I'll have to actually understand the wave formatting and write my own "formatter"...

Re: Is there any way to save sounddata to a file?

Posted: Wed Jan 02, 2019 6:23 am
by zorg
The samples would be the audio data you want to write out, contained within the sounddata;

Since i'm guessing write_samples_interlaced wants a lua table of values, you need to fill a table with the samplepoints you can get from the sounddata with the :getSample method (indexed from 0 to sounddata:getSampleCount-1), then, since that method returns values in the range of [-1.0,1.0], you need to convert that to signed 16bit integers;

The multiplication by 32767 will work for that, although you should restrict it with math.min and math.max as well (limits are -32768 and 32767 inclusive), and you should also math.floor it at the end, to be sure.

That said, if the bit depth would be 8, it would be either signed or unsigned bytes instead, it's in the specs i linked... interlaced means the channels' samplepoints are woven together, e.g.: left1, right1, left2, right2, etc...; löve already uses this format internally if the sounddata has two channels, but it shouldn't work too differently if there's only 1 channel, since there's nothing to interleave in that case.

(And i understand what the intention of that test code is, looks like a simple sine wave generator, but i have zero idea about why it does the sampling rate division thing it does... Edit: I misread the precedence on that, it just wraps it, but the division, imo should still be with 44100 and not 44099; the frequency is 500 Hz, but they decided to include 2pi in that variable as well, probably for performance reasons; it's only needed because they're using a trigonometric function to generate the samplepoints (sine), making the cycle be [0,1] instead of [0,2*pi].)

Re: Is there any way to save sounddata to a file?

Posted: Fri Jan 04, 2019 4:52 am
by Intas
zorg wrote: Wed Jan 02, 2019 6:23 amSince i'm guessing write_samples_interlaced wants a lua table of values, you need to fill a table with the samplepoints you can get from the sounddata with the :getSample method (indexed from 0 to sounddata:getSampleCount-1), then, since that method returns values in the range of [-1.0,1.0], you need to convert that to signed 16bit integers;
Ohhhh, right, that makes sense. I knew that :getSample existed but I didn't know how to convert those numbers to 16bit integers.

IT'S WORKING NOW! Thank you sooooooo much for your help zorg, you've been really helpful :awesome:

So my code looks like this, if there's something to add/remove/modify let me know.

Code: Select all

require("wav")
-- ...
local sounddata = rd:getData()
rd:stop()
local channelCount = sounddata:getChannelCount()
local sampleRate   = sounddata:getSampleRate()
local bitDepth     = sounddata:getBitDepth()
local samples = {}
for i = 0, sounddata:getSampleCount() - 1 do
	local sample = sounddata:getSample(i)
	local n
	local to16bit = sample * 32767
	if (to16bit > 0) then
		n = math.floor(math.min(to16bit, 32767))
	else
		n = math.floor(math.max(to16bit, -32768))
	end
	table.insert(samples, n)
end
local w = wav.create_context("test.wav", "w")
w.init(channelCount, sampleRate, bitDepth)
w.write_samples_interlaced(samples)
w.finish()
Now I'm going to edit wav.lua to make it löve compatible.

Again, thank you for your help (and patience :roll: all this audio stuff is new to me).

Re: [Solved] Is there any way to save sounddata to a file?

Posted: Tue Feb 05, 2019 2:15 pm
by ni_lus
Cool! I was looking for an info about this too..thanks!