diff --git a/slam/README.md b/slam/README.md new file mode 100644 index 0000000..6223feb --- /dev/null +++ b/slam/README.md @@ -0,0 +1,155 @@ +SLAM +==== +... is the **Simple [LÖVE] Audio Manager** formerly known as the +**Benignly Designed Sound Manager.** It's a minimally invasive +augmentation of [LÖVE]'s audio module. In contrast to sources that +can only have one simultaneous playing instance, SLAM sources +create *instances* when played. This way you can play one source +multiple times at once. Each instance will inherit the settings +(volume, speed, looping, ...) of it's SLAM source, but can +override them. + +SLAM also features tags, which can be used to modify a number of +sources at the same time. + +Example +------- + + require 'slam' + function love.load() + music = love.audio.newSource('music.ogg', 'stream') -- creates a new SLAM source + music:setLooping(true) -- all instances will be looping + music:setVolume(.3) -- set volume for all instances + love.audio.play(music) -- play music + + woosh = love.audio.newSource({'woosh1.ogg', 'woosh2.ogg'}, 'static') + end + + function love.keypressed() + local instance = woosh:play() -- creates a new instance + instance:setPitch(.5 + math.random() * .5) -- set pitch for this instance only + end + + +Reference +--------- + +### Operations on Sources + + source = love.audio.newSource(what, how) + +Returns a new SLAM source. Accepts the same parameters as +[love.audio.newSource](http://love2d.org/wiki/love.audio.newSource), with one major +difference: `what` can be a table, in which case each new playing instance will +pick an item of that table at random. + + + instance = love.audio.play(source) + instance = source:play() + +Plays a source and returns a handle to the player instance. Instances will inherit +the settings (looping, pitch, volume) of `source`. + + + love.audio.stop(source) + source:stop() + +Stops all playing instances of a source. + + + love.audio.stop() + +Stops all playing instances. + + + source:isStatic() + +Returns `true` if the source is static, `false` otherwise. + + + looping = source:isLooping() + source:setLooping(looping) + pitch = source:getPitch() + source:setPitch(pitch) + volume = source:getVolume() + source:setVolume(volume) + +Sets properties for all instances. Affects playing instances immediately. For details +on the parameters, see the [LÖVE wiki](http://love2d.org/wiki/Source). + + +### Instances + +All functions that affect LÖVE Sources can be applied to SLAM instances. These are: + + love.audio.pause(instance) + instance:pause() + instance:isPaused() + + love.audio.play(instance) + instance:play() + + love.audio.resume(instance) + instance:resume() + + love.audio.rewind(instance) + instance:rewind() + + instance:getDirection() + instance:setDirection() + + instance:getPitch() + instance:setPitch() + + instance:getPosition() + instance:setPosition() + + instance:getVelocity() + instance:setVelocity() + + instance:getVolume() + instance:setVolume() + + instance:isLooping() + instance:setLooping() + +See the [LÖVE wiki](http://love2d.org/wiki/Source) for details. + + +### Tags + +With tags you can group several sources together and call functions upon them. A +simple example might be this: + + -- add some stuff to the background tag + drums:addTags('music') + baseline:addTags('background', 'music') -- a source can have multiple tags + muttering:addTags('background') + noise:addTags('background') + cars:addTags('background') + + (...) + + love.audio.tags.background.setVolume(0) -- mute all background sounds + love.audio.tags.music.setVolume(.1) -- ... but keep the music alive + + +#### Functions + + source:addTags(tag, ...) + +Adds one or more tags to a source. By default, all sources are member of the tag `all`. + + + source:removeTags(tag, ...) + +Remove one or more tags from a source. + + + love.audio.tags.TAG.FUNCTION(...) + love.audio.tags[TAG].FUNCTION(...) + +Calls `FUNCTION` on all sources tagged with `TAG`. + + +[LÖVE]: http://love2d.org diff --git a/bdsm/bdsm.lua b/slam/bdsm.lua similarity index 100% rename from bdsm/bdsm.lua rename to slam/bdsm.lua diff --git a/slam/slam.lua b/slam/slam.lua new file mode 100644 index 0000000..fbe947b --- /dev/null +++ b/slam/slam.lua @@ -0,0 +1,166 @@ +-- Simple LÖVE Audio Manager +-- +-- Copyright (c) 2011 Matthias Richter +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- Except as contained in this notice, the name(s) of the above copyright holders +-- shall not be used in advertising or otherwise to promote the sale, use or +-- other dealings in this Software without prior written authorization. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +-- + +local newInstance = love.audio.newSource +local stop = love.audio.stop + +------------------ +-- source class -- +------------------ +local Source = {} +Source.__index = Source +Source.__newindex = error + +local function remove_stopped(sources) + local remove = {} + for s in pairs(sources) do + remove[s] = true + end + for s in pairs(remove) do + sources[s] = nil + end +end + +local function get_what(what) + if type(what) == 'table' then + return what[math.random(1,#what)] + end + return what +end + +local play_instance, stop_instance +function Source:play() + remove_stopped(self.instances) + local instance = newInstance(get_what(self.what), self.how) + + -- overwrite instance:stop() and instance:play() + if not (play_instance and stop_instance) then + play_instance = getmetatable(instance).play + getmetatable(instance).play = error + + stop_instance = getmetatable(instance).stop + getmetatable(instance).stop = function(this) + stop_instance(this) + self.instances[this] = nil + end + end + + instance:setLooping(self.looping) + instance:setPitch(self.pitch) + instance:setVolume(self.volume) + + self.instances[instance] = instance + play_instance(instance) + return instance +end + +function Source:stop() + for s in pairs(self.instances) do + s:stop() + end + self.instances = {} +end + +function Source:addTags(tag, ...) + if not tag then return end + love.audio.tags[tag][self] = self + return Source.addTags(self, ...) +end + +function Source:removeTags(tag, ...) + if not tag then return end + love.audio.tags[tag][self] = nil + return Source.removeTags(self, ...) +end + +function Source:isStatic() + return how ~= "stream" +end + +-- getter/setter for looping, pitch and volume +for _, property in ipairs{'looping', 'pitch', 'volume'} do + local name = property:sub(1,1):upper() .. property:sub(2) + Source['get' .. name] = function(self) + return self[property] + end + + Source['set' .. name] = function(self, val) + self[property] = val + for s in pairs(self.instances) do + s['set' .. name](s, val) + end + end +end +Source.isLooping = Source.getLooping + +-------------------------- +-- love.audio interface -- +-------------------------- +function love.audio.newSource(what, how) + local s = { + what = what, + how = how, + instances = {}, + looping = false, + pitch = 1, + volume = 1, + } + love.audio.tags.all[s] = s + return setmetatable(s, Source) +end + +function love.audio.play(what) + assert(what and what.instances, "Can only play source objects.") + return what:play() +end + +function love.audio.stop(what) + if what and what.stop then return what:stop() end + stop() +end + +---------- +-- tags -- +---------- +local Tag = { __mode = "kv" } +function Tag:__index(func) + -- calls a function on all tagged sources + return function(...) + for s in pairs(self) do + assert(type(s[func]) == "function", ("`%s' does not name a function."):format(func)) + s[func](s, ...) + end + end +end + +love.audio.tags = setmetatable({}, { + __newindex = error, + __index = function(t,k) + local tag = setmetatable({}, Tag) + rawset(t, k, tag) + return tag + end, +})