Hello
I'm attempting to parse a file that contains raw sound data as part of it. I've managed to get individual samples out of it by creating a DataView from the ByteData object that hold all the sample data, and wanted to test to see if I was pulling the right section out by playing back one of the samples, however I can't figure out how to do that without manually setting each individual sample through a loop. Is there a way, either through some function I don't know about or ffi trickery with the data pointers to copy large chunks of data directly into a SoundData?
Setting large amounts of SoundData at a time
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Re: Setting large amounts of SoundData at a time
I've figured out a roundabout solution, which is to take the chunk of SoundData and put a .wav file header in front of it, then put that in a FileData and put *that* into a SoundData, however for some reason the sample is not looping when I play it back with looping enabled. It plays back the audio just fine though, and I'm able to use it mostly like a normal audio source, but isPlaying() doesn't ever return false unless I stop it manually. This makes me worried that the header hasn't been set up correctly (I've been following this: http://soundfile.sapp.org/doc/WaveFormat/ to set it up), so ideally it would be great to just copy the sample data directly into a SoundData if that is possible. I don't have time right now, but later after I'm done moving I'll compare the generated wav "file" with an actual .wav file to see if maybe I'm missing some piece of data to make it loop properly.
Re: Setting large amounts of SoundData at a time
Hi, I think it's something like this (untested):
Code: Select all
local ffi = require('ffi')
local all_samples = love.filesystem.read(path)
local all_samples_buf = love.data.newByteData(all_samples)
-- Create a blank SoundData based on settings you already know.
local my_sounddata = love.sound.newSoundData(TOTAL_SAMPLES, SAMPLE_RATE,
BIT_DEPTH, CHANNELS)
-- Zero-based index into the byte to start copying from.
local byte_offset = 3316
-- How many bytes per sample (assuming the platform byte is 8 bits long).
local bytes_per_sample = BIT_DEPTH / 8
local destination_ptr = my_sounddata:getFFIPointer()
local source_ptr = all_samples_buf:getFFIPointer()
ffi.copy(destination_ptr,
source_ptr + byte_offset,
TOTAL_SAMPLES * bytes_per_sample)
https://luajit.org/ext_ffi_api.html#ffi_copy
Also note there's an extension included in LuaJIT, string.buffer, which is helpful when parsing files or buffer-like things. The functions buffer:set() and buffer:ref() are the most useful IMO.
Re: Setting large amounts of SoundData at a time
Attempting to add directly to a pointer like that gives me but casting to a uint64_t and back to a void * worked, thanks!
Code: Select all
attempt to perform arithmetic on 'void *' and 'number'
Re: Setting large amounts of SoundData at a time
Ah of course, thanks for the follow-up.
LuaJIT does support pointer arithmetic, so I think if you cast (or rather, construct) the void* into a byte pointer (uint8_t*) then it should work, without having to convert it back.
That is:
...with the rest as in the original, including that add with "byte_offset".
LuaJIT does support pointer arithmetic, so I think if you cast (or rather, construct) the void* into a byte pointer (uint8_t*) then it should work, without having to convert it back.
That is:
Code: Select all
local TYPEOF_UINT8_T = ffi.typeof("uint8_t *")
(...)
local source_ptr = all_samples_buf:getFFIPointer()
source_ptr = TYPEOF_UINT8_T(source_ptr)
- zorg
- Party member
- Posts: 3465
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: Setting large amounts of SoundData at a time
I might be wrong about this, but can't you just create a SoundData object directly from the DataView? (FileData should work, not sure about ByteData nor DataView objects)DTmg wrote: ↑Sun Aug 18, 2024 2:13 am I've managed to get individual samples out of it by creating a DataView from the ByteData object that hold all the sample data, and wanted to test to see if I was pulling the right section out by playing back one of the samples, however I can't figure out how to do that without manually setting each individual sample through a loop.
Of course, you'd still need to pass in some extra info (sampling rate, bit depth, channel count), but that's literally just a constructor in a one-liner.
Me and my stuff True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Re: Setting large amounts of SoundData at a time
I tried from just a DataView/ByteData and it wasn't working right, it seemed to only want FileData. In any case I did get it working!
Re: Setting large amounts of SoundData at a time
I am currently writing an MOD and HSC player and therefore had to deal with a similar problem.
You can use setSample to insert generated raw data into SoundData.
Here is the code for continuous buffered playback. But it only has 1 channel.
Now when you want to mix channels it works like this. I setup 9 channels.
You can use setSample to insert generated raw data into SoundData.
Here is the code for continuous buffered playback. But it only has 1 channel.
Code: Select all
-- Create a QueueableSource with the desired sample rate, bit depth, and channel count
local queueSource = love.audio.newQueueableSource(44100, 16, 1)
-- TEST SOUND
function createSound(samplerate, frequency)
local samples = {}
local amplitude = 0.5
for i = 1, samplerate do
samples[i] = amplitude * math.sin((i / 44100) * frequency * 2 * math.pi)
end
return samples
end
function generateRandomSound()
-- Generate random samples
local samples = createSound(44100, 385)
local audioData = love.sound.newSoundData(#samples, 44100, 16, 1)
for i, v in ipairs(samples) do
audioData:setSample(i - 1, v) -- i - 1 because SoundData is 0-indexed
end
-- Queue the generated audio data
queueSource:queue(audioData)
end
function love.update(dt)
-- Continuously generate and queue sound while maintaining playback
if queueSource:getFreeBufferCount() > 0 then
generateRandomSound()
end
-- Start playing if not already
if not queueSource:isPlaying() then
queueSource:play()
end
end
function love.draw()
love.graphics.print("Playing continuous random sound ...", 10, 10)
end
Code: Select all
-- Create 9 QueueableSources with the desired sample rate, bit depth, and channel count
local channels = {}
local numChannels = 9
local sampleRate = 44100
local bitDepth = 16
for i = 1, numChannels do
channels[i] = love.audio.newQueueableSource(sampleRate, bitDepth, 1)
end
-- Function to create a sine wave sound for a specific channel
function createSound(samplerate, frequency)
local samples = {}
local amplitude = 0.5
for i = 1, samplerate do
samples[i] = amplitude * math.sin((i / 44100) * frequency * 2 * math.pi)
end
return samples
end
-- Function to generate sounds for each channel and mix them together
function generateMixedSound()
local mixedSamples = {}
for i = 1, sampleRate do
mixedSamples[i] = 0 -- Initialize the mixed sample to 0
end
-- Generate and mix sounds from each channel
for ch = 1, numChannels do
local frequency = love.math.random(1,200) + ch * 50 -- Vary the frequency for each channel
local samples = createSound(sampleRate, frequency)
for i = 1, #samples do
mixedSamples[i] = mixedSamples[i] + (samples[i] / numChannels) -- Mix the sound, normalize by number of channels
end
end
-- Create SoundData for the mixed samples
local audioData = love.sound.newSoundData(#mixedSamples, sampleRate, bitDepth, 1)
for i, v in ipairs(mixedSamples) do
audioData:setSample(i - 1, v) -- SoundData is 0-indexed
end
-- Queue the mixed audio data in each channel
for ch = 1, numChannels do
if channels[ch]:getFreeBufferCount() > 0 then
channels[ch]:queue(audioData)
end
end
end
function love.update(dt)
-- Continuously generate and queue mixed sound while maintaining playback
generateMixedSound()
-- Start playing each channel if not already playing
for ch = 1, numChannels do
if not channels[ch]:isPlaying() then
channels[ch]:play()
end
end
end
function love.draw()
love.graphics.print("Playing mixed sound from 9 channels...", 10, 10)
end
- zorg
- Party member
- Posts: 3465
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: Setting large amounts of SoundData at a time
That sine wave implementation will pop at the ends unless you taper it to zero, or keep a continuous phase value that doesn't reset at the end of each sounddata.
Me and my stuff True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Re: Setting large amounts of SoundData at a time
Ah thanks zorg! It was just a sample to show how continous sampling works.
Who is online
Users browsing this forum: No registered users and 4 guests