Tweaking audio

This commit is contained in:
yaw-man 2023-01-15 09:39:58 -04:00
parent f1873abed7
commit c5407266eb
10 changed files with 283 additions and 84 deletions

111
audio.lua Normal file
View File

@ -0,0 +1,111 @@
local love = love
local passChimes = {}
local failChimes = {}
local drones = {}
local N = 8
local function StartDrones()
love.audio.setEffect( "droneReverb", {
type = "reverb",
volume = 1.0, }
)
drones.bass = love.audio.newSource("sounds/drone.flac", "stream")
drones.alto = love.audio.newSource("sounds/drone.flac", "stream")
drones.bass:setVolume( 0 )
drones.bass:setPitch( 0.25 )
drones.alto:setVolume( 0 )
drones.bass:setPitch( 0.5 )
drones.bass:setEffect( "droneReverb", true )
love.audio.play( drones.bass )
love.audio.play( drones.alto )
end
love.audio.setEffect( "passChimeReverb", {
type = "reverb",
volume = 1.0, } )
local passChime = love.audio.newSource("sounds/chimeb.flac", "static")
passChime:setEffect( "passChimeReverb", true )
local failChime = love.audio.newSource("sounds/chime.flac", "static")
failChime:setEffect( "passChimeReverb", true )
for i = 1, N do
passChimes[i] = passChime:clone()
failChimes[i] = failChime:clone()
end
local idxNextPassChime = 1
local idxNextFailChime = 1
local function GetNextPassChime( pass )
idxNextPassChime = idxNextPassChime % N + 1
return passChimes[ idxNextPassChime ]
end
local function GetNextFailChime( pass )
idxNextFailChime = idxNextFailChime % N + 1
return failChimes[ idxNextFailChime ]
end
local pitches =
{
1,
2/3,
3/2,
9/8,
27/16,
81/64,
243/128,
2 --Safety
}
local function GetPitch( th )
th = 0.5 + 0.5 * th / math.pi
return pitches[ 1 + math.floor( th * 7 )]
end
local function OnImpact( impact, score, pass, level )
--
if not pass then
--drones.bass:seek( level )
-- drones.alto:seek( level )
end
local th = impact.th
local r = impact.r
-- Chimes
local chime = pass and GetNextPassChime() or GetNextFailChime()
chime:setPitch( GetPitch( th ) )
chime:setPosition( r * math.cos( th ), r * math.sin( th ) )
local highgain = math.max( 0, math.min( score, 1.0 ) - (pass and 0.0 or 0.5))
chime:setFilter{
type = "lowpass",
volume = 0.5 * impact.speed,
highgain = highgain,
}
love.audio.stop( chime )
love.audio.play( chime )
end
--score is the hypothetical "beat score" that would be attained
--if an impact took place at this instant.
local function Update( score )
drones.bass:setVolume( 0.5 * math.sqrt( score ) )
--drones.alto:setVolume( 0.3 * score )
end
local function Reset( )
for i = 1, N do
love.audio.stop( passChimes[i] )
love.audio.stop( failChimes[i] )
end
StartDrones()
end
Reset()
return { OnImpact = OnImpact, Update = Update, Reset = Reset}

173
main.lua
View File

@ -4,12 +4,12 @@ local sitelenpona
local text local text
local marble local marble
local wave local wave
local particles
local audio
local recorder
local DetectCollision local DetectCollision
local OnImpact
local sounds = { local OnVictory
}
local state local state
@ -52,17 +52,52 @@ function love.load()
love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink. love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
--love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue. --love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue.
sounds.goodPing = love.audio.newSource("sounds/soundTest.ogg", "static")
sounds.badPing = love.audio.newSource("sounds/chime8.ogg", "static") do--particle system setup
particles = love.graphics.newParticleSystem(
love.graphics.newImage( "prideflag.png" ),
1024)
particles:setSizes( 0.00015, 0.0001, 0.00018 )
particles:setSizeVariation( 1 )
particles:setRadialAcceleration( 0, 0.5 )
particles:setSpeed( 0.2, 1 )
particles:setLinearDamping( 1, 10 )
particles:setRelativeRotation( true )
particles:setEmitterLifetime( -1 )
particles:setParticleLifetime( 0, 15 )
particles:setSpread( 0.05 )
particles:setColors(
1, 1, 1, 1,
91 / 255, 206 / 255, 250 / 255, 1,
245 / 255, 169 / 255, 184 / 255, 1,
1,1,1,0
)
end
sitelenpona = assert( require "sitelenpona" ) sitelenpona = assert( require "sitelenpona" )
text = assert( require "text" ) text = assert( require "text" )
marble = assert( require "marble" ) marble = assert( require "marble" )
wave = assert( require "wave" ) wave = assert( require "wave" )
DetectCollision = assert ( require "collision" ) DetectCollision = assert ( require "collision" )
audio = assert( require "audio" )
recorder = assert( require "recorder" )
return state.Reset() return state.Reset()
end end
local function ExtrapolateBeatScore( )
local t = love.timer.getTime()
local beat = state.beat
if not beat.t then return 2.0 end
if not beat.mu then return 2.0 end
if beat.mu < 0.001 then return 2.0 end
t = 1.1 * math.sin( math.pi * ( t - beat.t ) / beat.mu )
return t * t * t * t
end
local function BeatScore( t ) local function BeatScore( t )
local beat = state.beat local beat = state.beat
@ -82,63 +117,61 @@ local function BeatScore( t )
return 2.0 return 2.0
end end
local score = 1.1 * math.sin( math.pi * dt / beat.mu )
score = score * score * score * score
--General case: update average beat length. --General case: update average beat length.
local WEIGHT = 0.25 --High number makes the last beat more significant. if dt > 0.2 then
local mu = ( 1.0 - WEIGHT ) * beat.mu + WEIGHT * dt
beat.mu = mu local WEIGHT = 0.85 --High number makes the last beat more significant.
local mu = ( 1.0 - WEIGHT ) * beat.mu + WEIGHT * dt
beat.mu = mu
--Safety: avoid a dbz.
if mu < 0.001 or dt < 0.001 then
return 0.0
--error( "DBZ! Beat length too small." )
end
--Calculate beat score.
local score = dt * dt / ( mu * mu )
local TOLERANCE = 1.04
if dt < mu then
return TOLERANCE * score
else
return TOLERANCE / score
end end
return score
end end
local function OnVictory() OnImpact = function( impact )
end
local function OnImpact( impact )
if not impact then return end if not impact then return end
local score = BeatScore( impact.t ) local score = BeatScore( impact.t )
--DEBUG --DEBUG
state.lastBeatScore = score state.lastBeatScore = score
local pass = false
local sound if score > 1.0 then
if score > state.beatScoreThreshold then
sound = sounds.goodPing
state.beatScoreThreshold = 1.0 pass = true
state.currentBeat = state.currentBeat + 1 state.currentBeat = state.currentBeat + 1
if state.currentBeat >= 120 then if state.currentBeat >= 120 then
return OnVictory() return OnVictory()
end end
else
sound = sounds.badPing
state.beatScoreThreshold = state.beatScoreThreshold - 0.05
end end
love.audio.play( sound )
local x, y = math.cos(impact.th), math.sin(impact.th)
particles:setPosition( impact.r * x, impact.r * y )
particles:setEmissionArea( "normal", 0.1, 0.2, impact.th, true )
particles:emit( 50 * score * score )
audio.OnImpact( impact, score, pass, state.currentBeat )
marble.OnImpact( impact ) marble.OnImpact( impact )
wave.OnImpact( impact ) wave.OnImpact( impact )
end end
local _OnImpact = OnImpact
local function NewGame()
OnImpact = _OnImpact
state.Reset()
marble.Reset()
wave.Reset()
end
OnVictory = function()
OnImpact = function() end
end
function love.draw() function love.draw()
@ -149,35 +182,41 @@ function love.draw()
love.graphics.print( state.beatScoreThreshold, 0, 20) love.graphics.print( state.beatScoreThreshold, 0, 20)
love.graphics.print( state.lastBeatScore, 0, 30 )]] love.graphics.print( state.lastBeatScore, 0, 30 )]]
local score = ExtrapolateBeatScore()
love.graphics.push( "transform" ) love.graphics.push( "transform" )
love.graphics.applyTransform( transform ) love.graphics.applyTransform( transform )
wave.Draw()
if debugRenderImpact then
love.graphics.setLineWidth( 0.01 )
love.graphics.setColor( 1, 0, 0, 0.5 ) --Red: Incoming wave.Draw( score )
love.graphics.line(
debugRenderImpact.xi, love.graphics.setColor( 1.0, 1.0, 1.0, 1.0 )
debugRenderImpact.yi, love.graphics.draw(particles, 0, 0)
debugRenderImpact.xf,
debugRenderImpact.yf) --[[if debugRenderImpact then
love.graphics.setColor( 0, 1, 0, 0.5 ) --Green: Normal
love.graphics.line( love.graphics.setLineWidth( 0.01 )
debugRenderImpact.xi, love.graphics.setColor( 1, 0, 0, 0.5 ) --Red: Incoming
debugRenderImpact.yi,
debugRenderImpact.xn,
debugRenderImpact.yn)
love.graphics.setColor( 0, 0, 1, 0.5 ) -- Blue: Outgoing
love.graphics.line( love.graphics.line(
debugRenderImpact.xi, debugRenderImpact.xi,
debugRenderImpact.yi, debugRenderImpact.yi,
debugRenderImpact.vxout, debugRenderImpact.xf,
debugRenderImpact.vyout) debugRenderImpact.yf)
love.graphics.setColor( 0, 1, 0, 0.5 ) --Green: Normal
love.graphics.line(
debugRenderImpact.xi,
debugRenderImpact.yi,
debugRenderImpact.xn,
debugRenderImpact.yn)
love.graphics.setColor( 0, 0, 1, 0.5 ) -- Blue: Outgoing
love.graphics.line(
debugRenderImpact.xi,
debugRenderImpact.yi,
debugRenderImpact.vxout,
debugRenderImpact.vyout)
end end]]
love.graphics.pop() love.graphics.pop()
@ -191,8 +230,15 @@ end
function love.update( dt ) function love.update( dt )
audio.Update( ExtrapolateBeatScore() )
particles:update( dt )
dt = dt + state.timeToSimulate dt = dt + state.timeToSimulate
--Physics tick.
while dt > step do while dt > step do
recorder.Update( marble.GetAcceleration() ) --For savegames.
marble.Integrate( step ) marble.Integrate( step )
wave.Integrate( step ) wave.Integrate( step )
@ -211,7 +257,8 @@ end
function love.keypressed( key, code, isRepeat ) function love.keypressed( key, code, isRepeat )
if key == "escape" then return love.event.quit() end if key == "escape" then return love.event.quit() end
if key == "return" then return OnImpact{ t = love.timer.getTime() } end if key == "return" then return OnVictory() end
if key == "space" then return NewGame() end
return marble.OnKey() return marble.OnKey()
end end

View File

@ -4,14 +4,18 @@ local oldBuffer = love.graphics.newCanvas()
local newBuffer = love.graphics.newCanvas() local newBuffer = love.graphics.newCanvas()
local oldState, curState, newState local oldState, curState, newState
local FRICTION = 0.01 local FRICTION = 0.05
local MAXSPEED = 5.0 local MAXSPEED = 4
local ddx, ddy = 0.0, 0.0 local ddx, ddy = 0.0, 0.0
local function State( ) local function State( )
return { t = 0, x = 0, y = 0, dx = 0, dy = 0 } return { t = 0, x = 0, y = 0, dx = 0, dy = 0 }
end end
local function GetAcceleration( )
return ddx, ddy
end
local function Current() return curState end local function Current() return curState end
local function Next() return newState end local function Next() return newState end
@ -60,13 +64,13 @@ local function OnImpact( impact )
inward * (- math.sin(impact.th) ) - (1.0 - inward) * ( x * s + vy * c ) inward * (- math.sin(impact.th) ) - (1.0 - inward) * ( x * s + vy * c )
curState.x, curState.y = x, y curState.x, curState.y = x, y
curState.dx, curState.dy = 0.5 * vxout, 0.5 * vyout curState.dx, curState.dy = vxout, vyout
debugRenderImpact = { xi = x, yi = y, xf = x - vx, yf = y - vy, debugRenderImpact = { xi = x, yi = y, xf = x - vx, yf = y - vy,
xn = 0.2 * unx + x, yn = 0.2 * uny + y, xn = 0.2 * unx + x, yn = 0.2 * uny + y,
vxout = x + vxout, vyout = y + vyout} vxout = x + vxout, vyout = y + vyout}
return Integrate( math.max( impact.dt , 1 / 60 ) ) --Hmm! Maybe this should be a fixed step instead for stability's sake. return Integrate( math.max( impact.dt , 1 / 120 ) ) --Hmm! Maybe this should be a fixed step instead for stability's sake.
end end
local function Update() local function Update()
@ -120,7 +124,7 @@ local function Draw()
love.graphics.draw( newBuffer ) love.graphics.draw( newBuffer )
love.graphics.setColor( 1, 1, 1, 1.0 ) --White. love.graphics.setColor( 1, 1, 1, 1.0 ) --White.
love.graphics.setLineWidth( 1.0 ) love.graphics.setLineWidth( 1.0 )
love.graphics.circle( "line", xn, yn, 4 ) --love.graphics.circle( "line", xn, yn, 4 )
oldBuffer, newBuffer = newBuffer, oldBuffer oldBuffer, newBuffer = newBuffer, oldBuffer
@ -159,4 +163,5 @@ return {
Resize = Resize, Resize = Resize,
Current = Current, Current = Current,
Next = Next, Next = Next,
GetAcceleration = GetAcceleration,
} }

BIN
prideflag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

32
recorder.lua Normal file
View File

@ -0,0 +1,32 @@
--Record demos.
local love = love
local recorder = {}
recorder.isLoaded = false
local i = 0
local function Reset()
i = 0
end
function recorder.Update( ddx, ddy )
local byte = 0
if ddx > 0.5 then byte = byte + 1 end
if ddy > 0.5 then byte = byte + 2 end
if ddx < -0.5 then byte = byte + 4 end
if ddy < -0.5 then byte = byte + 8 end
i = i + 1
recorder[i] = string.char( byte )
end
function recorder.Load( )
local s = love.filesystem.read( "demo" )
recorder.isLoaded = true
end
function recorder.Save( )
assert( love.filesystem.write( "demo", table.concat( recorder ) ) )
end
return recorder

BIN
sounds/chime.flac Normal file

Binary file not shown.

Binary file not shown.

BIN
sounds/chimeb.flac Normal file

Binary file not shown.

BIN
sounds/drone.flac Normal file

Binary file not shown.

View File

@ -2,7 +2,7 @@
local love = love local love = love
local N = 33 local N = 33
local SOUNDSPEED = 0.5 local SOUNDSPEED = 0.5
local IMPULSESIZE = 20 local IMPULSESIZE = 5
local DAMPING = 0.01 local DAMPING = 0.01
@ -20,6 +20,7 @@ local shader = love.graphics.newShader([[
uniform float re[33]; uniform float re[33];
uniform float im[33]; uniform float im[33];
uniform float score;
//Slow IDFT //Slow IDFT
float r( float x ) float r( float x )
@ -47,7 +48,7 @@ local shader = love.graphics.newShader([[
float r = r( atan(p.y, p.x) ) - length( p ); float r = r( atan(p.y, p.x) ) - length( p );
return vec4(color.x, color.y, color.z, (r > -0.01)) ; return vec4( (1.0 + float(score > 1.0) * r * 0.1) * color.xyz, float(r > -0.01)) ;
} }
]]) ]])
@ -175,8 +176,6 @@ local function Wave( )
local t = { local t = {
--radii[k] = radius of point on curve at angle (k - 1) / N --radii[k] = radius of point on curve at angle (k - 1) / N
radii = {}, radii = {},
--TIME derivative of radius
vrad = {},
--SPACE DFT of radius function (which is periodic) --SPACE DFT of radius function (which is periodic)
dftre = {}, dftre = {},
@ -184,21 +183,21 @@ local function Wave( )
} }
for i = 1, N do for i = 1, N do
t.radii[i] = 1.0 + 0.05 * ( i/N + math.sin( i * 2.0 * math.pi / N )) t.radii[i] = 1.0
t.vrad[i] = 0.0
end end
DFT( t ) DFT( t )
return setmetatable(t, mt) return setmetatable(t, mt)
end end
local function Draw() local function Draw( score )
-- Blue circle. -- Blue circle.
love.graphics.setColor( 91 / 255, 206 / 255, 250 / 255 ) love.graphics.setColor( 91 / 255, 206 / 255, 250 / 255 )
shader:send( "re", unpack( cur.dftre ) ) shader:send( "re", unpack( cur.dftre ) )
shader:send( "im", unpack( cur.dftim ) ) shader:send( "im", unpack( cur.dftim ) )
shader:send( "score", score )
love.graphics.setShader( shader ) love.graphics.setShader( shader )
love.graphics.circle("fill", 0, 0, 1.5) love.graphics.circle("fill", 0, 0, 1.5)
local t = love.timer.getTime() local t = love.timer.getTime()
@ -253,7 +252,7 @@ local function Draw()
end end
local function Update() local function Update( )
--Deep copy of current state to old state. --Deep copy of current state to old state.
for name, t in pairs( cur ) do for name, t in pairs( cur ) do
@ -290,6 +289,11 @@ local function DetectCollision( px, py, vpx, vpy )
end end
local function Reset() local function Reset()
SOUNDSPEED = 0.5
IMPULSESIZE = 5
DAMPING = 0.02
old = Wave() old = Wave()
cur = Wave() cur = Wave()
new = Wave() new = Wave()