chore: clean recommit
This commit is contained in:
parent
8024c6ac40
commit
60ad2e7fb1
122 changed files with 23950 additions and 323 deletions
306
Examples/Audio/StreamCallback.cs
Normal file
306
Examples/Audio/StreamCallback.cs
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [audio] example - stream callback
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
* Example originally created with raylib 6.0, last time updated with raylib 6.0
|
||||
*
|
||||
* Example created by Dan Hoang (@dan-hoang) and reviewed by Ramon Santamaria (@raysan5)
|
||||
*
|
||||
* NOTE: Example sends a wave to the audio device,
|
||||
* user gets the choice of four waves: sine, square, triangle, and sawtooth
|
||||
* A stream is set up to play to the audio device; stream is hooked to a callback that
|
||||
* generates a wave, that is determined by user choice
|
||||
*
|
||||
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||
* BSD-like license that allows static linking with closed source software
|
||||
*
|
||||
* Copyright (c) 2026 Dan Hoang (@dan-hoang)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Audio;
|
||||
|
||||
[ExcludeFromBrowser("SetAudioStreamCallback is unreliable on the wasm audio backend")]
|
||||
public unsafe partial class StreamCallback : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int BUFFER_SIZE = 4096;
|
||||
private const int SAMPLE_RATE = 44100;
|
||||
|
||||
// Wave type
|
||||
private enum WaveType
|
||||
{
|
||||
Sine,
|
||||
Square,
|
||||
Triangle,
|
||||
Sawtooth
|
||||
}
|
||||
|
||||
public string Name => "Audio / Stream Callback";
|
||||
|
||||
public string Title => "raylib [audio] example - stream callback";
|
||||
|
||||
public int TargetFps => 30;
|
||||
|
||||
private static int waveFrequency = 440;
|
||||
private static int newWaveFrequency = 440;
|
||||
private static int waveIndex = 0;
|
||||
|
||||
// Buffer to keep the last second of uploaded audio,
|
||||
// part of which will be drawn on the screen
|
||||
private static readonly float[] buffer = new float[SAMPLE_RATE];
|
||||
|
||||
private static readonly string[] waveTypesAsString = { "sine", "square", "triangle", "sawtooth" };
|
||||
|
||||
private AudioStream stream;
|
||||
private WaveType waveType;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
InitAudioDevice();
|
||||
|
||||
waveFrequency = 440;
|
||||
newWaveFrequency = 440;
|
||||
waveIndex = 0;
|
||||
Array.Clear(buffer, 0, buffer.Length);
|
||||
|
||||
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
|
||||
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
|
||||
|
||||
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
|
||||
stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
|
||||
PlayAudioStream(stream);
|
||||
|
||||
// Configure it so that the callback for waveType is called whenever stream is out of samples
|
||||
waveType = WaveType.Sine;
|
||||
SetWaveCallback();
|
||||
}
|
||||
|
||||
// Attach the callback matching the current waveType to the stream
|
||||
private void SetWaveCallback()
|
||||
{
|
||||
switch (waveType)
|
||||
{
|
||||
case WaveType.Sine: SetAudioStreamCallback(stream, &SineCallback); break;
|
||||
case WaveType.Square: SetAudioStreamCallback(stream, &SquareCallback); break;
|
||||
case WaveType.Triangle: SetAudioStreamCallback(stream, &TriangleCallback); break;
|
||||
case WaveType.Sawtooth: SetAudioStreamCallback(stream, &SawtoothCallback); break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
if (IsKeyDown(KeyboardKey.Up))
|
||||
{
|
||||
newWaveFrequency += 10;
|
||||
if (newWaveFrequency > 12500) newWaveFrequency = 12500;
|
||||
}
|
||||
|
||||
if (IsKeyDown(KeyboardKey.Down))
|
||||
{
|
||||
newWaveFrequency -= 10;
|
||||
if (newWaveFrequency < 20) newWaveFrequency = 20;
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KeyboardKey.Left))
|
||||
{
|
||||
if (waveType == WaveType.Sine) waveType = WaveType.Sawtooth;
|
||||
else if (waveType == WaveType.Square) waveType = WaveType.Sine;
|
||||
else if (waveType == WaveType.Triangle) waveType = WaveType.Square;
|
||||
else waveType = WaveType.Triangle;
|
||||
|
||||
SetWaveCallback();
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KeyboardKey.Right))
|
||||
{
|
||||
if (waveType == WaveType.Sine) waveType = WaveType.Square;
|
||||
else if (waveType == WaveType.Square) waveType = WaveType.Triangle;
|
||||
else if (waveType == WaveType.Triangle) waveType = WaveType.Sawtooth;
|
||||
else waveType = WaveType.Sine;
|
||||
|
||||
SetWaveCallback();
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
DrawText($"frequency: {newWaveFrequency}", screenWidth - 220, 10, 20, Color.Red);
|
||||
DrawText($"wave type: {waveTypesAsString[(int)waveType]}", screenWidth - 220, 30, 20, Color.Red);
|
||||
DrawText("Up/down to change frequency", 10, 10, 20, Color.DarkGray);
|
||||
DrawText("Left/right to change wave type", 10, 30, 20, Color.DarkGray);
|
||||
|
||||
// Draw the last 10 ms of uploaded audio
|
||||
for (int i = 0; i < screenWidth; i++)
|
||||
{
|
||||
Vector2 startPos = new Vector2(i, 250 - 50 * buffer[WaveSampleIndex(i)]);
|
||||
Vector2 endPos = new Vector2(i + 1, 250 - 50 * buffer[WaveSampleIndex(i + 1)]);
|
||||
DrawLineV(startPos, endPos, Color.Red);
|
||||
}
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM
|
||||
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
|
||||
}
|
||||
|
||||
// Maps a screen column to a sample index in the last 10 ms of the buffer. The final column
|
||||
// maps one sample past the end of the buffer; upstream C reads out of bounds there, so we
|
||||
// clamp to the last valid sample (visually identical, but safe under C# bounds checking).
|
||||
private static int WaveSampleIndex(int column)
|
||||
{
|
||||
int index = SAMPLE_RATE - SAMPLE_RATE / 100 + column * SAMPLE_RATE / 100 / screenWidth;
|
||||
return index < SAMPLE_RATE ? index : SAMPLE_RATE - 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//------------------------------------------------------------------------------------
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void SineCallback(void* framesOut, uint frameCount)
|
||||
{
|
||||
int fc = (int)frameCount;
|
||||
int wavelength = SAMPLE_RATE / waveFrequency;
|
||||
float* frames = (float*)framesOut;
|
||||
|
||||
// Synthesize the sine wave
|
||||
for (int i = 0; i < fc; i++)
|
||||
{
|
||||
frames[i] = MathF.Sin(2 * MathF.PI * waveIndex / wavelength);
|
||||
|
||||
waveIndex++;
|
||||
|
||||
if (waveIndex >= wavelength)
|
||||
{
|
||||
waveFrequency = newWaveFrequency;
|
||||
waveIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the synthesized samples for later drawing
|
||||
for (int i = 0; i < SAMPLE_RATE - fc; i++) buffer[i] = buffer[i + fc];
|
||||
for (int i = 0; i < fc; i++) buffer[SAMPLE_RATE - fc + i] = frames[i];
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void SquareCallback(void* framesOut, uint frameCount)
|
||||
{
|
||||
int fc = (int)frameCount;
|
||||
int wavelength = SAMPLE_RATE / waveFrequency;
|
||||
float* frames = (float*)framesOut;
|
||||
|
||||
// Synthesize the square wave
|
||||
for (int i = 0; i < fc; i++)
|
||||
{
|
||||
frames[i] = (waveIndex < wavelength / 2) ? 1 : -1;
|
||||
waveIndex++;
|
||||
|
||||
if (waveIndex >= wavelength)
|
||||
{
|
||||
waveFrequency = newWaveFrequency;
|
||||
waveIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the synthesized samples for later drawing
|
||||
for (int i = 0; i < SAMPLE_RATE - fc; i++) buffer[i] = buffer[i + fc];
|
||||
for (int i = 0; i < fc; i++) buffer[SAMPLE_RATE - fc + i] = frames[i];
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void TriangleCallback(void* framesOut, uint frameCount)
|
||||
{
|
||||
int fc = (int)frameCount;
|
||||
int wavelength = SAMPLE_RATE / waveFrequency;
|
||||
float* frames = (float*)framesOut;
|
||||
|
||||
// Synthesize the triangle wave
|
||||
for (int i = 0; i < fc; i++)
|
||||
{
|
||||
frames[i] = (waveIndex < wavelength / 2) ? (-1 + 2.0f * waveIndex / (wavelength / 2)) : (1 - 2.0f * (waveIndex - wavelength / 2) / (wavelength / 2));
|
||||
waveIndex++;
|
||||
|
||||
if (waveIndex >= wavelength)
|
||||
{
|
||||
waveFrequency = newWaveFrequency;
|
||||
waveIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the synthesized samples for later drawing
|
||||
for (int i = 0; i < SAMPLE_RATE - fc; i++) buffer[i] = buffer[i + fc];
|
||||
for (int i = 0; i < fc; i++) buffer[SAMPLE_RATE - fc + i] = frames[i];
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void SawtoothCallback(void* framesOut, uint frameCount)
|
||||
{
|
||||
int fc = (int)frameCount;
|
||||
int wavelength = SAMPLE_RATE / waveFrequency;
|
||||
float* frames = (float*)framesOut;
|
||||
|
||||
// Synthesize the sawtooth wave
|
||||
for (int i = 0; i < fc; i++)
|
||||
{
|
||||
frames[i] = -1 + 2.0f * waveIndex / wavelength;
|
||||
waveIndex++;
|
||||
|
||||
if (waveIndex >= wavelength)
|
||||
{
|
||||
waveFrequency = newWaveFrequency;
|
||||
waveIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the synthesized samples for later drawing
|
||||
for (int i = 0; i < SAMPLE_RATE - fc; i++) buffer[i] = buffer[i + fc];
|
||||
for (int i = 0; i < fc; i++) buffer[SAMPLE_RATE - fc + i] = frames[i];
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream callback");
|
||||
|
||||
SetTargetFPS(30);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new StreamCallback();
|
||||
game.Init();
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
game.Update();
|
||||
}
|
||||
|
||||
game.Unload();
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue