aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2023-09-05 03:33:52 +0300
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2023-09-05 23:39:23 +0300
commit8ac5f63d63b298bb729dedde02e8c50d0efb17f4 (patch)
tree1d93d7878893fd5af91167d7f38cb8071d081316
parenteae78cb206f38b5f4334b621bce54c3ba9048abc (diff)
downloadapplause2-8ac5f63d63b298bb729dedde02e8c50d0efb17f4.tar.gz
libsndfile related classes moved into sndfile-stream.lua
-rw-r--r--applause.lua101
-rw-r--r--sndfile-stream.lua100
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