changeset 854:48a2ac8bec07

did: some more audio functionality, some unfinished grid-mesh code
author Sam <sam@basx.dev>
date Tue, 26 Dec 2023 15:16:34 +0700
parents f5009fbb2cbc
children 1ecc948dbacd
files semicongine/audio.nim semicongine/core/audiotypes.nim semicongine/mesh.nim tests/test_audio.nim
diffstat 4 files changed, 103 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/audio.nim	Sat Dec 09 01:06:58 2023 +0700
+++ b/semicongine/audio.nim	Tue Dec 26 15:16:34 2023 +0700
@@ -23,6 +23,7 @@
     loop: bool
     levelLeft: Level
     levelRight: Level
+    paused: bool
   Track = object
     playing: Table[uint64, Playback]
     level: Level
@@ -80,7 +81,8 @@
       position: 0,
       loop: loop,
       levelLeft: levelLeft,
-      levelRight: levelRight
+      levelRight: levelRight,
+      paused: false,
     )
   result = mixer.playbackCounter
   inc mixer.playbackCounter
@@ -133,16 +135,45 @@
         track.playing.del(playbackId)
         break
 
+proc pause*(mixer: var Mixer, value: bool) =
+  mixer.lock.withLock():
+    for track in mixer.tracks.mvalues:
+      for playback in track.playing.mvalues:
+        playback.paused = value
+
+proc pause*(mixer: var Mixer, track: string, value: bool) =
+  mixer.lock.withLock():
+    for playback in mixer.tracks[track].playing.mvalues:
+      playback.paused = value
+
+proc pause*(mixer: var Mixer, playbackId: uint64, value: bool) =
+  mixer.lock.withLock():
+    for track in mixer.tracks.mvalues:
+      if playbackId in track.playing:
+        track.playing[playbackId].paused = value
+
+proc pause*(mixer: var Mixer) = mixer.pause(true)
+proc pause*(mixer: var Mixer, track: string) = mixer.pause(track, true)
+proc pause*(mixer: var Mixer, playbackId: uint64) = mixer.pause(playbackId, true)
+proc unpause*(mixer: var Mixer) = mixer.pause(false)
+proc unpause*(mixer: var Mixer, track: string) = mixer.pause(track, false)
+proc unpause*(mixer: var Mixer, playbackId: uint64) = mixer.pause(playbackId, false)
+
 proc isPlaying*(mixer: var Mixer): bool =
   mixer.lock.withLock():
     for track in mixer.tracks.mvalues:
-      if track.playing.len > 0:
-        return true
+      for playback in track.playing.values:
+        if not playback.paused:
+          return true
   return false
 
 proc isPlaying*(mixer: var Mixer, track: string): bool =
   mixer.lock.withLock():
-    return mixer.tracks.contains(track) and mixer.tracks[track].playing.len > 0
+    if mixer.tracks.contains(track):
+      for playback in mixer.tracks[track].playing.values:
+        if not playback.paused:
+          return true
+    return false
 
 func applyLevel(sample: Sample, levelLeft, levelRight: Level): Sample =
   [int16(float(sample[0]) * levelLeft), int16(float(sample[1]) * levelRight)]
@@ -160,17 +191,19 @@
 proc updateSoundBuffer(mixer: var Mixer) =
   # mix
   for i in 0 ..< mixer.buffers[mixer.currentBuffer].len:
-    var currentSample = [0'i16, 0'i16]
+    var mixedSample = [0'i16, 0'i16]
     mixer.lock.withLock():
       for track in mixer.tracks.mvalues:
         var stoppedSounds: seq[uint64]
         for (id, playback) in track.playing.mpairs:
+          if playback.paused:
+            continue
           let sample = applyLevel(
             playback.sound[][playback.position],
             mixer.level * track.level * playback.levelLeft,
             mixer.level * track.level * playback.levelRight,
           )
-          currentSample = mix(currentSample, sample)
+          mixedSample = mix(mixedSample, sample)
           inc playback.position
           if playback.position >= playback.sound[].len:
             if playback.loop:
@@ -179,11 +212,42 @@
               stoppedSounds.add id
         for id in stoppedSounds:
           track.playing.del(id)
-      mixer.buffers[mixer.currentBuffer][i] = currentSample
+      mixer.buffers[mixer.currentBuffer][i] = mixedSample
   # send data to sound device
   mixer.device.writeSoundData(mixer.currentBuffer)
   mixer.currentBuffer = (mixer.currentBuffer + 1) mod mixer.buffers.len
 
+# DSP functions
+
+proc lowPassFilter(data: var SoundData, cutoff: int) =
+  let alpha = float(cutoff) / AUDIO_SAMPLE_RATE
+  var value = data[0]
+  for i in 0 ..< data.len:
+    value[0] += int16(alpha * float(data[i][0] - value[0]))
+    value[1] += int16(alpha * float(data[i][1] - value[1]))
+    data[i] = value
+
+proc downsample(data: var SoundData, n: int) =
+  let newLen = (data.len - 1) div n + 1
+  for i in 0 ..< newLen:
+    data[i] = data[i * n]
+  data.setLen(newLen)
+
+proc upsample(data: var SoundData, m: int) =
+  data.setLen(data.len * m)
+  var i = data.len - 1
+  while i < 0:
+    if i mod m == 0:
+      data[i] = data[i div m]
+    else:
+      data[i] = [0, 0]
+    i.dec
+
+proc slowdown(data: var SoundData, m, n: int) =
+  data.upsample(m)
+  # TODO
+  # data.lowPassFilter(m)
+  data.downsample(n)
 
 proc destroy*(mixer: var Mixer) =
   mixer.lock.deinitLock()
--- a/semicongine/core/audiotypes.nim	Sat Dec 09 01:06:58 2023 +0700
+++ b/semicongine/core/audiotypes.nim	Tue Dec 26 15:16:34 2023 +0700
@@ -18,12 +18,12 @@
     sin(x * 2 * Pi * f)
   result = ret
 
-proc sineSoundData*(f: float, len: float, rate: int): SoundData =
+proc sineSoundData*(f: float, len: float, rate: int, amplitude = 0.5'f32): SoundData =
   let dt = 1'f / float(rate)
   var sine = sinewave(f)
   for i in 0 ..< int(float(rate) * len):
     let t = dt * float(i)
-    let value = int16(sine(t) * float(high(int16)))
+    let value = int16(sine(t) * float(high(int16)) * amplitude)
     result.add [value, value]
 
 proc newSound*(data: SoundData): Sound =
--- a/semicongine/mesh.nim	Sat Dec 09 01:06:58 2023 +0700
+++ b/semicongine/mesh.nim	Tue Dec 26 15:16:34 2023 +0700
@@ -502,6 +502,29 @@
   result[].initVertexAttribute(DEFAULT_POSITION_ATTRIBUTE, pos)
   result[].initVertexAttribute("color", col)
 
+proc grid*(rows, columns, cellSize=1.0'f32, color="ffffffff", material=EMPTY_MATERIAL.initMaterialData()): Mesh =
+  
+  result = Mesh(
+    vertexCount: rows * columns + (rows + columns + 1),
+    instanceTransforms: @[Unit4F32],
+    indexType: Small,
+    name: &"grid-{instanceCounter}",
+    material: material,
+  )
+  inc instanceCounter
+
+  var
+    pos = @[newVec3f(0, 0)]
+    col = @[c, c]
+  for h in 0 ..< rows:
+    for w in 0 ..< columns:
+      pos.add newVec3f(cos(float32(i) * step) * half_w, sin(float32(i) * step) * half_h)
+      col.add c
+      result[].smallIndices.add [uint16(0), uint16(i + 1), uint16(i + 2)]
+
+  result[].initVertexAttribute(DEFAULT_POSITION_ATTRIBUTE, pos)
+  result[].initVertexAttribute("color", col)
+
 # MESH TREES =============================================================================
 
 type
--- a/tests/test_audio.nim	Sat Dec 09 01:06:58 2023 +0700
+++ b/tests/test_audio.nim	Tue Dec 26 15:16:34 2023 +0700
@@ -14,10 +14,11 @@
   let s2 = mixer[].play("test2", loop=true)
 
   let t0 = now()
+  mixer[].setLevel(0.5)
   while true:
     let runtime = (now() - t0).inMilliseconds()
     if runtime > 1500:
-      mixer[].setLevel(0.1)
+      mixer[].setLevel(0.2)
     if runtime > 3000:
       mixer[].stop(s2)
     if runtime > 6000:
@@ -72,9 +73,9 @@
 
 when isMainModule:
   startMixerThread()
-  test1()
+  # test1()
+  # mixer[].stop()
+  # test2()
+  # mixer[].stop()
+  test3()
   mixer[].stop()
-  test2()
-  mixer[].stop()
-  # test3()
-  # mixer[].stop()