|
|
|
@ -1,56 +1,25 @@ |
|
|
|
/******************************************************************************************* |
|
|
|
* |
|
|
|
* raylib [audio] example - raw stream |
|
|
|
* |
|
|
|
* Example complexity rating: [★★★☆] 3/4 |
|
|
|
* |
|
|
|
* Example originally created with raylib 1.6, last time updated with raylib 4.2 |
|
|
|
* |
|
|
|
* Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) |
|
|
|
* |
|
|
|
* 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) 2015-2025 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) |
|
|
|
* |
|
|
|
********************************************************************************************/ |
|
|
|
* |
|
|
|
* raylib [audio] example - raw stream |
|
|
|
* |
|
|
|
* Example complexity rating: [★★★☆] 3/4 |
|
|
|
* |
|
|
|
* Example originally created with raylib 1.6, last time updated with raylib 4.2 |
|
|
|
* |
|
|
|
* Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) |
|
|
|
* |
|
|
|
* 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) 2015-2025 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) |
|
|
|
* |
|
|
|
********************************************************************************************/ |
|
|
|
|
|
|
|
#include "raylib.h" |
|
|
|
#include <math.h> |
|
|
|
|
|
|
|
#include <stdlib.h> // Required for: malloc(), free() |
|
|
|
#include <math.h> // Required for: sinf() |
|
|
|
#include <string.h> // Required for: memcpy() |
|
|
|
|
|
|
|
#define MAX_SAMPLES 512 |
|
|
|
#define MAX_SAMPLES_PER_UPDATE 4096 |
|
|
|
|
|
|
|
// Cycles per second (hz) |
|
|
|
float frequency = 440.0f; |
|
|
|
|
|
|
|
// Audio frequency, for smoothing |
|
|
|
float audioFrequency = 440.0f; |
|
|
|
|
|
|
|
// Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency |
|
|
|
float oldFrequency = 1.0f; |
|
|
|
|
|
|
|
// Index for audio rendering |
|
|
|
float sineIdx = 0.0f; |
|
|
|
|
|
|
|
// Audio input processing callback |
|
|
|
void AudioInputCallback(void *buffer, unsigned int frames) |
|
|
|
{ |
|
|
|
audioFrequency = frequency + (audioFrequency - frequency)*0.95f; |
|
|
|
|
|
|
|
float incr = audioFrequency/44100.0f; |
|
|
|
short *d = (short *)buffer; |
|
|
|
|
|
|
|
for (unsigned int i = 0; i < frames; i++) |
|
|
|
{ |
|
|
|
d[i] = (short)(32000.0f*sinf(2*PI*sineIdx)); |
|
|
|
sineIdx += incr; |
|
|
|
if (sineIdx > 1.0f) sineIdx -= 1.0f; |
|
|
|
} |
|
|
|
} |
|
|
|
#define BUFFER_SIZE 4096 |
|
|
|
#define SAMPLE_RATE 44100 |
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------ |
|
|
|
// Program main entry point |
|
|
|
@ -63,44 +32,25 @@ int main(void) |
|
|
|
const int screenHeight = 450; |
|
|
|
|
|
|
|
InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream"); |
|
|
|
SetTargetFPS(30); |
|
|
|
|
|
|
|
InitAudioDevice(); // Initialize audio device |
|
|
|
InitAudioDevice(); |
|
|
|
|
|
|
|
SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE); |
|
|
|
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE |
|
|
|
SetAudioStreamBufferSizeDefault(BUFFER_SIZE); |
|
|
|
float buffer[BUFFER_SIZE] = {}; |
|
|
|
|
|
|
|
// Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono) |
|
|
|
AudioStream stream = LoadAudioStream(44100, 16, 1); |
|
|
|
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono) |
|
|
|
AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1); |
|
|
|
float pan = 0.0f; |
|
|
|
SetAudioStreamPan(stream, pan); |
|
|
|
PlayAudioStream(stream); |
|
|
|
|
|
|
|
SetAudioStreamCallback(stream, AudioInputCallback); |
|
|
|
int sineFrequency = 440; |
|
|
|
int newSineFrequency = 440; |
|
|
|
int sineIndex = 0; |
|
|
|
double sineStartTime = 0.0; |
|
|
|
|
|
|
|
// Buffer for the single cycle waveform we are synthesizing |
|
|
|
short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES); |
|
|
|
|
|
|
|
// Frame buffer, describing the waveform when repeated over the course of a frame |
|
|
|
short *writeBuf = (short *)malloc(sizeof(short)*MAX_SAMPLES_PER_UPDATE); |
|
|
|
|
|
|
|
PlayAudioStream(stream); // Start processing stream buffer (no data loaded currently) |
|
|
|
|
|
|
|
// Position read in to determine next frequency |
|
|
|
Vector2 mousePosition = { -100.0f, -100.0f }; |
|
|
|
|
|
|
|
/* |
|
|
|
// Cycles per second (hz) |
|
|
|
float frequency = 440.0f; |
|
|
|
|
|
|
|
// Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency |
|
|
|
float oldFrequency = 1.0f; |
|
|
|
|
|
|
|
// Cursor to read and copy the samples of the sine wave buffer |
|
|
|
int readCursor = 0; |
|
|
|
*/ |
|
|
|
|
|
|
|
// Computed size in samples of the sine wave |
|
|
|
int waveLength = 1; |
|
|
|
|
|
|
|
Vector2 position = { 0, 0 }; |
|
|
|
|
|
|
|
SetTargetFPS(30); // Set our game to run at 30 frames-per-second |
|
|
|
//-------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
// Main game loop |
|
|
|
@ -108,92 +58,84 @@ int main(void) |
|
|
|
{ |
|
|
|
// Update |
|
|
|
//---------------------------------------------------------------------------------- |
|
|
|
mousePosition = GetMousePosition(); |
|
|
|
|
|
|
|
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) |
|
|
|
if (IsKeyDown(KEY_UP)) |
|
|
|
{ |
|
|
|
float fp = (float)(mousePosition.y); |
|
|
|
frequency = 40.0f + (float)(fp); |
|
|
|
newSineFrequency += 10; |
|
|
|
|
|
|
|
t">float pan = (float)(mousePosition.x)/(float)screenWidth; |
|
|
|
SetAudioStreamPan(stream, pan); |
|
|
|
">if (newSineFrequency > 12500) |
|
|
|
newSineFrequency = 12500; |
|
|
|
} |
|
|
|
|
|
|
|
// Rewrite the sine wave |
|
|
|
// Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc. |
|
|
|
if (frequency != oldFrequency) |
|
|
|
if (IsKeyDown(KEY_DOWN)) |
|
|
|
{ |
|
|
|
// Compute wavelength. Limit size in both directions |
|
|
|
//int oldWavelength = waveLength; |
|
|
|
waveLength = (int)(22050/frequency); |
|
|
|
if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; |
|
|
|
if (waveLength < 1) waveLength = 1; |
|
|
|
|
|
|
|
// Write sine wave |
|
|
|
for (int i = 0; i < waveLength*2; i++) |
|
|
|
{ |
|
|
|
data[i] = (short)(sinf(((2*PI*(float)i/waveLength)))*32000); |
|
|
|
} |
|
|
|
// Make sure the rest of the line is flat |
|
|
|
for (int j = waveLength*2; j < MAX_SAMPLES; j++) |
|
|
|
{ |
|
|
|
data[j] = (short)0; |
|
|
|
} |
|
|
|
newSineFrequency -= 10; |
|
|
|
|
|
|
|
// Scale read cursor's position to minimize transition artifacts |
|
|
|
//readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength)); |
|
|
|
oldFrequency = frequency; |
|
|
|
if (newSineFrequency < 20) |
|
|
|
newSineFrequency = 20; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
// Refill audio stream if required |
|
|
|
if (IsAudioStreamProcessed(stream)) |
|
|
|
if (IsKeyDown(KEY_LEFT)) |
|
|
|
{ |
|
|
|
// Synthesize a buffer that is exactly the requested size |
|
|
|
int writeCursor = 0; |
|
|
|
|
|
|
|
while (writeCursor < MAX_SAMPLES_PER_UPDATE) |
|
|
|
{ |
|
|
|
// Start by trying to write the whole chunk at once |
|
|
|
int writeLength = MAX_SAMPLES_PER_UPDATE-writeCursor; |
|
|
|
|
|
|
|
// Limit to the maximum readable size |
|
|
|
int readLength = waveLength-readCursor; |
|
|
|
pan -= 0.01f; |
|
|
|
SetAudioStreamPan(stream, pan); |
|
|
|
|
|
|
|
if (writeLength > readLength) writeLength = readLength; |
|
|
|
if (pan < -1.0f) |
|
|
|
pan = -1.0f; |
|
|
|
} |
|
|
|
|
|
|
|
// Write the slice |
|
|
|
memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short)); |
|
|
|
if (IsKeyDown(KEY_RIGHT)) |
|
|
|
{ |
|
|
|
pan += 0.01f; |
|
|
|
SetAudioStreamPan(stream, pan); |
|
|
|
|
|
|
|
// Update cursors and loop audio |
|
|
|
readCursor = (readCursor + writeLength)%waveLength; |
|
|
|
if (pan > 1.0f) |
|
|
|
pan = 1.0f; |
|
|
|
} |
|
|
|
|
|
|
|
writeCursor += writeLength; |
|
|
|
if (IsAudioStreamProcessed(stream)) |
|
|
|
{ |
|
|
|
for (int i = 0; i < BUFFER_SIZE; i++) |
|
|
|
{ |
|
|
|
int wavelength = SAMPLE_RATE/sineFrequency; |
|
|
|
buffer[i] = sin(2*PI*sineIndex/wavelength); |
|
|
|
sineIndex++; |
|
|
|
|
|
|
|
if (sineIndex >= wavelength) |
|
|
|
{ |
|
|
|
sineFrequency = newSineFrequency; |
|
|
|
sineIndex = 0; |
|
|
|
sineStartTime = GetTime(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Copy finished frame to audio stream |
|
|
|
UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE); |
|
|
|
UpdateAudioStream(stream, buffer, BUFFER_SIZE); |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
// Draw |
|
|
|
//---------------------------------------------------------------------------------- |
|
|
|
BeginDrawing(); |
|
|
|
|
|
|
|
ClearBackground(RAYWHITE); |
|
|
|
|
|
|
|
DrawText(TextFormat("sine frequency: %i",(int)frequency), GetScreenWidth() - 220, 10, 20, RED); |
|
|
|
DrawText("click mouse button to change frequency or pan", 10, 10, 20, DARKGRAY); |
|
|
|
|
|
|
|
// Draw the current buffer state proportionate to the screen |
|
|
|
for (int i = 0; i < screenWidth; i++) |
|
|
|
{ |
|
|
|
position.x = (float)i; |
|
|
|
position.y = 250 + 50*data[i*MAX_SAMPLES/screenWidth]/32000.0f; |
|
|
|
|
|
|
|
DrawPixelV(position, RED); |
|
|
|
} |
|
|
|
ClearBackground(RAYWHITE); |
|
|
|
|
|
|
|
DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); |
|
|
|
DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); |
|
|
|
DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); |
|
|
|
DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); |
|
|
|
|
|
|
|
int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; |
|
|
|
int windowSize = 0.1f*SAMPLE_RATE; |
|
|
|
int wavelength = SAMPLE_RATE/sineFrequency; |
|
|
|
|
|
|
|
// Draw a sine wave with the same frequency as the one being sent to the audio stream |
|
|
|
for (int i = 0; i < screenWidth; i++) { |
|
|
|
int t0 = windowStart + i*windowSize/screenWidth; |
|
|
|
int t1 = windowStart + (i + 1)*windowSize/screenWidth; |
|
|
|
Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) }; |
|
|
|
Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) }; |
|
|
|
DrawLineV(startPos, endPos, RED); |
|
|
|
} |
|
|
|
|
|
|
|
EndDrawing(); |
|
|
|
//---------------------------------------------------------------------------------- |
|
|
|
@ -201,9 +143,6 @@ int main(void) |
|
|
|
|
|
|
|
// De-Initialization |
|
|
|
//-------------------------------------------------------------------------------------- |
|
|
|
free(data); // Unload sine wave data |
|
|
|
free(writeBuf); // Unload write buffer |
|
|
|
|
|
|
|
UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM |
|
|
|
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) |
|
|
|
|
|
|
|
|