packpico/QueueableSource.lua

229 lines
4.9 KiB
Lua

--[[
love-microphone
QueueableSource.lua
Provides a QueueableSource object, pseudo-inheriting from Source.
See http://love2d.org/wiki/Source for better documentation on
most methods except queue and new.
]]
local ffi = require("ffi")
local al = require("openal")
local QueueableSource = {}
local typecheck = {
Object = true,
QueueableSource = true
}
--[[
alFormat getALFormat(SoundData data)
data: The SoundData to query.
Returns the correct alFormat enum value for the given SoundData.
]]
local function getALFormat(data)
local stereo = data:getChannelCount() == 2
local deep = data:getBitDepth() == 16
if (stereo) then
if (deep) then
return al.AL_FORMAT_STEREO16
else
return al.AL_FORMAT_STEREO8
end
end
if (deep) then
return al.AL_FORMAT_MONO16
else
return al.AL_FORMAT_MONO8
end
end
--[[
QueueableSource QueueableSource:new(uint bufferCount=16)
bufferCount: The number of buffers to use to hold queued sounds.
Creates a new QueueableSource object.
]]
function QueueableSource:new(bufferCount)
if (bufferCount) then
if (type(bufferCount) ~= "number" or bufferCount % 1 ~= 0 or bufferCount < 0) then
return nil, "Invalid argument #1: bufferCount must be a positive integer if given."
end
else
bufferCount = 16
end
local new = {}
for key, value in pairs(self) do
if (key ~= "new") then
new[key] = value
end
end
local pBuffers = ffi.new("ALuint[?]", bufferCount)
al.alGenBuffers(bufferCount, pBuffers)
local freeBuffers = {}
for i = 0, bufferCount - 1 do
table.insert(freeBuffers, pBuffers[i])
end
local pSource = ffi.new("ALuint[1]")
al.alGenSources(1, pSource)
al.alSourcei(pSource[0], al.AL_LOOPING, 0)
new._bufferCount = bufferCount
new._pBuffers = pBuffers
new._freeBuffers = freeBuffers
new._source = pSource[0]
new._pAvailable = ffi.new("ALint[1]")
new._pBufferHolder = ffi.new("ALuint[16]")
local wrapper = newproxy(true)
getmetatable(wrapper).__index = new
getmetatable(wrapper).__gc = function(self)
al.alSourceStop(new._source)
al.alSourcei(new._source, al.AL_BUFFER, 0)
al.alDeleteSources(1, pSource)
al.alDeleteBuffers(bufferCount, pBuffers)
end
return wrapper
end
--[[
string QueueableSource:type()
Returns the string name of the class, "QueueableSource".
]]
function QueueableSource:type()
return "QueueableSource"
end
--[[
bool QueueableSource:typeOf(string type)
type: The type to check against.
Returns whether the object matches the given type.
]]
function QueueableSource:typeOf(type)
return typecheck[type]
end
--[[
void QueueableSource:queue(SoundData data) (Success)
(void, string) QueueableSource:queue(SoundData data) (Failure)
data: The SoundData to queue for playback.
Queues a new SoundData to play.
Will fail and return nil and an error message if no buffers were available.
]]
function QueueableSource:queue(data)
self:step()
if (#self._freeBuffers == 0) then
return nil, "No free buffers were available to playback the given audio."
end
local top = table.remove(self._freeBuffers, 1)
al.alBufferData(top, getALFormat(data), data:getPointer(), data:getSize(), data:getSampleRate())
al.alSourceQueueBuffers(self._source, 1, ffi.new("ALuint[1]", top))
end
--[[
void QueueableSource:step()
Opens up queues that have been used.
Called automatically by queue.
]]
function QueueableSource:step()
al.alGetSourcei(self._source, al.AL_BUFFERS_PROCESSED, self._pAvailable)
if (self._pAvailable[0] > 0) then
al.alSourceUnqueueBuffers(self._source, self._pAvailable[0], self._pBufferHolder)
for i = 0, self._pAvailable[0] - 1 do
table.insert(self._freeBuffers, self._pBufferHolder[i])
end
end
end
--[[
void QueueableSource:clear()
Stops playback and clears all queued data.
]]
function QueueableSource:clear()
self:pause()
for i = 0, self._bufferCount - 1 do
al.alSourceUnqueueBuffers(self._source, self._bufferCount, self._pBuffers)
table.insert(self._freeBuffers, self._pBuffers[i])
end
end
--[[
uint QueueableSource:getFreeBufferCount()
Returns the number of free buffers for queueing sounds with this QueueableSource.
]]
function QueueableSource:getFreeBufferCount()
return #self._freeBuffers
end
--[[
void QueueableSource:play()
Begins playing audio.
]]
function QueueableSource:play()
if (not self:isPlaying()) then
al.alSourcePlay(self._source)
end
end
--[[
bool QueueableSource:isPlaying()
Returns whether the source is playing audio.
]]
function QueueableSource:isPlaying()
local state = ffi.new("ALint[1]")
al.alGetSourcei(self._source, al.AL_SOURCE_STATE, state)
return (state[0] == al.AL_PLAYING)
end
--[[
void QueueableSource:pause()
Stops playing audio.
]]
function QueueableSource:pause()
if (not self:isPaused()) then
al.alSourcePause(self._source)
end
end
--[[
void QueueableSource:isPaused()
Returns whether the source is paused.
]]
function QueueableSource:isPaused()
local state = ffi.new("ALint[1]")
al.alGetSourcei(self._source, al.AL_SOURCE_STATE, state)
return (state[0] == al.AL_PAUSED)
end
return QueueableSource