diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-09-05 03:33:52 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-09-05 23:39:23 +0300 |
commit | 8ac5f63d63b298bb729dedde02e8c50d0efb17f4 (patch) | |
tree | 1d93d7878893fd5af91167d7f38cb8071d081316 | |
parent | eae78cb206f38b5f4334b621bce54c3ba9048abc (diff) | |
download | applause2-8ac5f63d63b298bb729dedde02e8c50d0efb17f4.tar.gz |
libsndfile related classes moved into sndfile-stream.lua
-rw-r--r-- | applause.lua | 101 | ||||
-rw-r--r-- | sndfile-stream.lua | 100 |
2 files changed, 101 insertions, 100 deletions
diff --git a/applause.lua b/applause.lua index fa59168..342264c 100644 --- a/applause.lua +++ b/applause.lua @@ -1,4 +1,3 @@ -local sndfile = require "sndfile" local bit = require "bit" local ffi = require "ffi" -- According to LuaJIT docs, it makes sense to cache @@ -542,39 +541,6 @@ function Stream:jdump(opt, outfile) if err then error(err) end end --- TODO: Use a buffer to improve perfomance (e.g. 1024 samples) -function Stream:save(filename, format) - if self:len() == math.huge then - error("Cannot save infinite stream") - end - - local channels = self.channels - local hnd = sndfile:new(filename, "SFM_WRITE", - samplerate, channels, format) - - local frame_buffer = sndfile.frame_type(channels) - - self:foreach(function(frame) - -- NOTE: This should be (hopefully) automatically - -- unrolled for single-channel streams - -- Otherwise each loop copies an entire frame. - -- This should be faster than letting LuaJIT translate - -- the frame directly. - for i = 1, channels do - local sample = tonumber(frame[i]) - assert(sample ~= nil) - frame_buffer[i-1] = sample - end - - -- NOTE: Apparently we cannot use hnd:write() if a frame is larger than one sample - -- (i.e. multichannel streams) - -- FIXME: Check return value - hnd:writef(frame_buffer) - end) - - hnd:close() -end - function Stream:tonumber() return self:map(tonumber) end function Stream:tostring() return self:map(tostring) end @@ -1024,72 +990,6 @@ function VectorStream:len() return #self.vector end -SndfileStream = DeriveClass(Stream) - -function SndfileStream:ctor(filename, samplerate, channels, format) - -- FIXME: This fails if the file is not at the - -- correct sample rate. Need to resample... - -- NOTE: samplerate and channels are ignored unless SF_FORMAT_RAW - -- files are read. - local handle = sndfile:new(filename, "SFM_READ", - samplerate, channels, format) - self.filename = filename - self.samplerate = handle.info.samplerate - self.channel_no = handle.info.channels - self.format = handle.info.format - self.frames = tonumber(handle.info.frames) - handle:close() - - if self.channel_no > 1 then - local cached = self:cache() - local streams = {} - for i = 0, self.channel_no-1 do - streams[i+1] = cached:map(function(frame) - return tonumber(frame[i]) - end) - end - return MuxStream:new(unpack(streams)) - end -end - -function SndfileStream:gtick() - -- The file is reopened, so each tick has an independent - -- read pointer which is important when reusing the stream. - -- NOTE: We could do this with a single handle per object but - -- by maintaining our own read position and seeking before reading. - local handle = sndfile:new(self.filename, "SFM_READ", - self.samplerate, self.channel_no, self.format) - - -- Make sure that we are still reading the same file; - -- at least with the same meta-data. - -- Theoretically, the file could have changed since object - -- construction. - assert(handle.info.channels == self.channel_no and - handle.info.frames == self.frames, - "Sndfile changed") - - if self.channel_no == 1 then - local read = handle.read - - return function() - return read(handle) - end - else - -- For multi-channel audio files, we generate a stream - -- of frame buffers. - -- However, the user never sees these since they are translated - -- to a MuxStream automatically (see ctor()) - local readf = handle.readf - local frame = sndfile.frame_type(self.channel_no) - - return function() - return readf(handle, frame) and frame or nil - end - end -end - -function SndfileStream:len() return self.frames end - ConcatStream = DeriveClass(MuxableStream) function ConcatStream:muxableCtor(...) @@ -1769,6 +1669,7 @@ Client.__gc = Client.kill -- Additional modules are loaded with dofile(), -- so they react to reload() -- +dofile "sndfile-stream.lua" dofile "filters.lua" dofile "dssi.lua" dofile "midi.lua" diff --git a/sndfile-stream.lua b/sndfile-stream.lua new file mode 100644 index 0000000..68ed5cc --- /dev/null +++ b/sndfile-stream.lua @@ -0,0 +1,100 @@ +local sndfile = require "sndfile" + +SndfileStream = DeriveClass(Stream) + +function SndfileStream:ctor(filename, samplerate, channels, format) + -- FIXME: This fails if the file is not at the + -- correct sample rate. Need to resample... + -- NOTE: samplerate and channels are ignored unless SF_FORMAT_RAW + -- files are read. + local handle = sndfile:new(filename, "SFM_READ", + samplerate, channels, format) + self.filename = filename + self.samplerate = handle.info.samplerate + self.channel_no = handle.info.channels + self.format = handle.info.format + self.frames = tonumber(handle.info.frames) + handle:close() + + if self.channel_no > 1 then + local cached = self:cache() + local streams = {} + for i = 0, self.channel_no-1 do + streams[i+1] = cached:map(function(frame) + return tonumber(frame[i]) + end) + end + return MuxStream:new(unpack(streams)) + end +end + +function SndfileStream:gtick() + -- The file is reopened, so each tick has an independent + -- read pointer which is important when reusing the stream. + -- NOTE: We could do this with a single handle per object but + -- by maintaining our own read position and seeking before reading. + local handle = sndfile:new(self.filename, "SFM_READ", + self.samplerate, self.channel_no, self.format) + + -- Make sure that we are still reading the same file; + -- at least with the same meta-data. + -- Theoretically, the file could have changed since object + -- construction. + assert(handle.info.channels == self.channel_no and + handle.info.frames == self.frames, + "Sndfile changed") + + if self.channel_no == 1 then + local read = handle.read + + return function() + return read(handle) + end + else + -- For multi-channel audio files, we generate a stream + -- of frame buffers. + -- However, the user never sees these since they are translated + -- to a MuxStream automatically (see ctor()) + local readf = handle.readf + local frame = sndfile.frame_type(self.channel_no) + + return function() + return readf(handle, frame) and frame or nil + end + end +end + +function SndfileStream:len() return self.frames end + +-- TODO: Use a buffer to improve perfomance (e.g. 1024 samples) +function Stream:save(filename, format) + if self:len() == math.huge then + error("Cannot save infinite stream") + end + + local channels = self.channels + local hnd = sndfile:new(filename, "SFM_WRITE", + samplerate, channels, format) + + local frame_buffer = sndfile.frame_type(channels) + + self:foreach(function(frame) + -- NOTE: This should be (hopefully) automatically + -- unrolled for single-channel streams + -- Otherwise each loop copies an entire frame. + -- This should be faster than letting LuaJIT translate + -- the frame directly. + for i = 1, channels do + local sample = tonumber(frame[i]) + assert(sample ~= nil) + frame_buffer[i-1] = sample + end + + -- NOTE: Apparently we cannot use hnd:write() if a frame is larger than one sample + -- (i.e. multichannel streams) + -- FIXME: Check return value + hnd:writef(frame_buffer) + end) + + hnd:close() +end |