<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged glaze</title>
<link>https://coderlegion.com/tag/glaze</link>
<description>Powered by Question2Answer</description>
<item>
<title>DevLog 20250611: Audio API Design for Divooka Glaze!</title>
<link>https://coderlegion.com/3324/devlog-20250611-audio-api-design-for-divooka-glaze</link>
<description>&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Glaze!&lt;/strong&gt; is an interactive media framework in Divooka that features a &lt;a rel=&quot;nofollow&quot; href=&quot;https://processing.org/&quot;&gt;Processing&lt;/a&gt;-like interface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;overview&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Today, I'm laying the groundwork for audio APIs for our &lt;strong&gt;Glaze!&lt;/strong&gt; framework.&lt;/p&gt;
&lt;p&gt;Divooka &lt;strong&gt;Glaze!&lt;/strong&gt; draws inspiration from SFML and Processing, so we need to understand how those two handle audio first.&lt;/p&gt;
&lt;h3 id=&quot;how-sfml-handles-it-sfml-net-&quot;&gt;How SFML Handles It (SFML.Net)&lt;/h3&gt;
&lt;p&gt;To play audio in SFML.Net, we use the &lt;strong&gt;SFML.Audio&lt;/strong&gt; module, which provides two main classes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Sound&lt;/code&gt;&lt;/strong&gt; + &lt;strong&gt;&lt;code&gt;SoundBuffer&lt;/code&gt;&lt;/strong&gt; for short, fully-loaded sounds (like sound effects).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Music&lt;/code&gt;&lt;/strong&gt; for long, streamed audio (like background music).&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;playing-short-sounds&quot;&gt;Playing Short Sounds&lt;/h4&gt;
&lt;p&gt;Short audio files (WAV, OGG, FLAC, etc.) can be fully loaded into memory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// Load the entire file into a buffer (only do this once per sound).
SoundBuffer buffer = new SoundBuffer(&quot;hit.wav&quot;);

// Create a Sound instance and assign the buffer.
Sound hitSound = new Sound(buffer);

// Optional settings
hitSound.Volume = 75f;    // 0–100
hitSound.Pitch  = 1.2f;   // playback speed
hitSound.Loop   = false;  // play once

// Play it
hitSound.Play();

// Make sure the program doesn't exit immediately,
// otherwise the sound will be cut off:
while (hitSound.Status == SoundStatus.Playing)
    System.Threading.Thread.Sleep(10);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reuse&lt;/strong&gt; the same &lt;code&gt;SoundBuffer&lt;/code&gt; if you need to play the same sound multiple times.&lt;/li&gt;
&lt;li&gt;Keep the &lt;code&gt;Sound&lt;/code&gt; (and its &lt;code&gt;SoundBuffer&lt;/code&gt;) in scope until playback finishes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;streaming-music&quot;&gt;Streaming Music&lt;/h4&gt;
&lt;p&gt;For large files (e.g., full-length tracks), use the &lt;code&gt;Music&lt;/code&gt; class, which streams from disk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It doesn't load the entire file into RAM.&lt;/li&gt;
&lt;li&gt;Suitable for long tracks, though with slightly higher CPU usage due to streaming.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// Create a Music instance; it will stream the file on the fly.
Music backgroundMusic = new Music(&quot;background.ogg&quot;);

// Optional settings
backgroundMusic.Volume = 50f;
backgroundMusic.Loop   = true;

// Start playback
backgroundMusic.Play();

// In a game loop we'd typically:
// while (window.IsOpen()) { /* update, draw, etc. */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;common-pitfalls&quot;&gt;Common Pitfalls&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Missing Native DLLs&lt;/strong&gt;: If you get &lt;code&gt;DllNotFoundException&lt;/code&gt;, ensure all CSFML native libraries are present.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scope &amp;amp; Lifetime&lt;/strong&gt;: If &lt;code&gt;Sound&lt;/code&gt; or &lt;code&gt;Music&lt;/code&gt; goes out of scope (is garbage collected), playback stops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Program Exit&lt;/strong&gt;: If the application ends before the audio finishes, you won't hear the full sound - keep the main thread running or integrate playback into the render loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;how-processing-handles-it&quot;&gt;How Processing Handles It&lt;/h3&gt;
&lt;p&gt;Processing offers sound support through the built-in Sound library and the legacy Minim library.&lt;/p&gt;
&lt;p&gt;Below are the two most common approaches.&lt;/p&gt;
&lt;h4 id=&quot;the-built-in-sound-library&quot;&gt;The Built-in Sound Library&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Installing/Importing&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the Processing IDE, go to &lt;strong&gt;Sketch → Import Library… → Add Library…&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;“Sound”&lt;/strong&gt;, then click &lt;strong&gt;Install&lt;/strong&gt; (if not already installed).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Placing Audio Files&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a folder named &lt;code&gt;data&lt;/code&gt; in the sketch's directory.&lt;/li&gt;
&lt;li&gt;Copy audio files (e.g., &lt;code&gt;music.mp3&lt;/code&gt; or &lt;code&gt;beat.wav&lt;/code&gt;) into that &lt;code&gt;data&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the sketch, import and declare:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import processing.sound.*;
SoundFile song;

void setup() {
  size(400, 200);
  // Load a file named &quot;music.mp3&quot; from /data
  song = new SoundFile(this, &quot;music.mp3&quot;);
  song.play();        // start playing once
}

void draw() {
  background(0);
  // (Optionally visualize or control playback here)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Looping, Volume, Rate&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;void setup() {
  size(400, 200);
  song = new SoundFile(this, &quot;music.mp3&quot;);
  
  song.loop();               // loop indefinitely
  song.amp(0.5);             // set volume (0.0–1.0)
  song.rate(1.2);            // change playback speed (1.0 = normal)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;minim&quot;&gt;Minim&lt;/h4&gt;
&lt;p&gt;Minim handles things similarly, albeit through &lt;code&gt;AudioPlayer&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import ddf.minim.*;
Minim minim;
AudioPlayer player;

void setup() {
  size(400, 200);
  
  minim = new Minim(this);
  // loadFile(filename, bufferSize)
  player = minim.loadFile(&quot;music.mp3&quot;, 2048); // Place MP3/WAV files inside the sketch's `data` folder.
  player.play();
  
  // Looping &amp;amp; Controlling
  player.loop();        // loop playback
  player.setGain(-10);  // lower volume: gain in dB
  player.setTempo(1.5); // speed multiplier
}

void draw() {
  background(50);
  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Where to Store Files&lt;/strong&gt;: Always place audio files in the sketch's &lt;code&gt;data/&lt;/code&gt; folder so Processing can find them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File Formats&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Sound library: supports WAV, AIFF, MP3 (depending on platform).&lt;/li&gt;
&lt;li&gt;Minim: supports MP3, OGG, WAV.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency / Real-Time&lt;/strong&gt;: For low-latency playback or live audio input/processing, Minim (or advanced libraries like &lt;strong&gt;Beads&lt;/strong&gt; or &lt;strong&gt;TarsosDSP&lt;/strong&gt;) may be preferable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visualizations&lt;/strong&gt;: Both libraries support real-time audio analysis (waveforms, FFT), which is great for interactive visuals.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;observations&quot;&gt;Observations&lt;/h3&gt;
&lt;p&gt;Those two statements are crucial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Load a soundfile from the /data folder of the sketch and play it back
file = new SoundFile(this, &quot;sample.mp3&quot;);
file.play();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can observe a few things regarding API design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It's procedural and imperative - we create resources first, then set properties, then play/stop via the resource reference. Importantly, it's necessary to keep a reference for later control.&lt;/li&gt;
&lt;li&gt;Resources might require explicit (or scope-based) release.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On the other hand, I'm more interested in a &quot;functional&quot; style API, like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6mgkkm5rdmftf7f1g7r.png&quot; alt=&quot;Audio API Concept&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-approach&quot;&gt;The Approach&lt;/h3&gt;
&lt;p&gt;We could implicitly load and manage resources, releasing them at the end of the application lifetime (on window close), if no explicit reference is needed. However, distinguishing between &lt;code&gt;SoundBuffer&lt;/code&gt; and &lt;code&gt;Sound&lt;/code&gt; (both are &lt;code&gt;IDisposable&lt;/code&gt;) can be tricky when deciding which to persist. At the very least, this is what we should expose via &lt;code&gt;PlaySound()&lt;/code&gt; and &lt;code&gt;PlayMusic()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ifkkz6igrkzxiyhb31l.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;private void ReleaseResources()
{
    // Sounds
    foreach (Sound item in _soundInstances)
        item.Dispose();
    foreach ((string key, SoundBuffer item) in _soundBuffers)
        item.Dispose();
    foreach (Music item in _music)
        item.Dispose();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In fact in the current design we cannot share the underlying instance - since &lt;strong&gt;Glaze!&lt;/strong&gt; is implementation-agnostic - unless we introduce strongly typed types for &lt;code&gt;SoundBuffer&lt;/code&gt;, &lt;code&gt;Sound&lt;/code&gt;, and &lt;code&gt;Music&lt;/code&gt;, so all implementations can recognize them. Otherwise, we can't return any instance objects.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// IInteractiveServiceProvider, which is ignorant of SFML.Net's existence
#region Audio Engine
void PlaySound(string filePath);
void PlayMusic(string filePath, bool loop = true);
#endregion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A slightly inelegant shortcut is to expose them as untyped &quot;object handles&quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;/// &amp;lt;summary&amp;gt;
/// One-shot sound.
/// &amp;lt;/summary&amp;gt;
object PlaySound(string filePath);
/// &amp;lt;summary&amp;gt;
/// Loopable BGM etc.
/// &amp;lt;/summary&amp;gt;
object PlayMusic(string filePath, bool loop = true);
/// &amp;lt;param name=&quot;volume&quot;&amp;gt;0–100f&amp;lt;/param&amp;gt;
void SetVolume(object soundOrMusic, float volume);
/// &amp;lt;param name=&quot;pitch&quot;&amp;gt;0–1f&amp;lt;/param&amp;gt;
void SetPitch(object soundOrMusic, float pitch);
void SetLoop(object music, bool loop);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it might seem &quot;unsafe&quot; on the paper, it works well in Divooka's graphical interface:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfrx2osj8j4zhwd8l1ue.png&quot; alt=&quot;Object Handle Looks Just Like Any Other Object on GUI - As If Strongly Typed&quot;&gt;&lt;/p&gt;
&lt;p&gt;(This is partly thanks to Divooka's support for return value naming via XML doc comments, e.g., &lt;code&gt;&amp;lt;returns name=&quot;Music&quot;&amp;gt;A music instance.&amp;lt;/returns&amp;gt;&lt;/code&gt; for &lt;code&gt;PlayMusic()&lt;/code&gt;.)&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So far so good! There's a lot we can enhance in the audio engine - more parameters, DSP support, etc. For example, Processing has very neat functions for &lt;a rel=&quot;nofollow&quot; href=&quot;https://processing.org/reference/libraries/sound/index.html&quot;&gt;low-pass/high-pass filters&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But for now, we can play sounds - and that's a solid start!&lt;/p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Processing Sound library documentation: &lt;a rel=&quot;nofollow&quot; href=&quot;https://processing.org/reference/libraries/sound/SoundFile_play_.html&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Divooka Glaze! framework documentation on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Glaze&quot;&gt;Methodox Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/3324/devlog-20250611-audio-api-design-for-divooka-glaze</guid>
<pubDate>Wed, 11 Jun 2025 18:14:04 +0000</pubDate>
</item>
</channel>
</rss>