mirror of
https://github.com/raysan5/raygui.git
synced 2025-12-25 10:22:33 -05:00
1596 lines
62 KiB
C
1596 lines
62 KiB
C
/*******************************************************************************************
|
|
*
|
|
* rFXGen 1.0 - raylib FX sounds generator (based on Tomas Petterson sfxr)
|
|
*
|
|
* DEPENDENCIES:
|
|
*
|
|
* raylib 1.6 - This program uses latest raylib audio module functionality.
|
|
* raygui 1.0 - Simple IMGUI library (based on raylib)
|
|
* tinyfiledialogs 2.5.8 - Open/save file dialogs, it requires linkage with comdlg32 and ole32 libs.
|
|
*
|
|
* VERSIONS HISTORY:
|
|
*
|
|
* 1.0 (XX-Sep-2016) TODO: Release stable version... not yet...
|
|
* 0.95 (14-Sep-2016) Reviewed comments and .rfx format
|
|
* 0.9 (12-Sep-2016) Defined WaveParams struct and command line functionality
|
|
* 0.8 (09-Sep-2016) Added open/save file dialogs using tinyfiledialogs library
|
|
* 0.7 (04-Sep-2016) Program variables renaming for consistency, code reorganized
|
|
* 0.6 (30-Aug-2016) Interface redesigned (reduced size) and new features added (wave drawing)
|
|
* 0.5 (27-Aug-2016) Completed port and adaptation from sfxr (only sound generation and playing)
|
|
*
|
|
* LICENSE: zlib/libpng
|
|
*
|
|
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
|
|
*
|
|
* This software is provided "as-is", without any express or implied warranty. In no event
|
|
* will the authors be held liable for any damages arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose, including commercial
|
|
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not claim that you
|
|
* wrote the original software. If you use this software in a product, an acknowledgment
|
|
* in the product documentation would be appreciated but is not required.
|
|
*
|
|
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
|
* as being the original software.
|
|
*
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
**********************************************************************************************/
|
|
|
|
#include "raylib.h"
|
|
|
|
//#define RAYGUI_STYLE_DEFAULT_DARK
|
|
#define RAYGUI_NO_STYLE_SAVE_LOAD // Avoid compiling style load/save code
|
|
#define RAYGUI_IMPLEMENTATION
|
|
#include "raygui.h"
|
|
|
|
#include "external/tinyfiledialogs.h" // Required for native open/save file dialogs
|
|
|
|
#include <math.h> // Required for: sinf(), pow()
|
|
#include <stdlib.h> // Required for: malloc(), free()
|
|
#include <string.h> // Required for: strcmp()
|
|
#include <stdio.h> // Required for: FILE, fopen(), fread(), fwrite(), ftell(), fseek() fclose()
|
|
// NOTE: Used on functions: LoadSound(), SaveSound(), WriteWAV()
|
|
|
|
#include "twitter.h" // Twitter icon embedded
|
|
|
|
#if defined(_WIN32)
|
|
#include <direct.h>
|
|
#define GetCurrentDir _getcwd
|
|
#else
|
|
#include <unistd.h>
|
|
#define GetCurrentDir getcwd
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Defines and Macros
|
|
//----------------------------------------------------------------------------------
|
|
#define MAX_WAVE_LENGTH 10 // 10 seconds
|
|
|
|
#define rnd(n) GetRandomValue(0, n)
|
|
#define frnd(range) ((float)rnd(10000)/10000.0f*range)
|
|
|
|
// Wave parameters type
|
|
typedef struct WaveParams {
|
|
|
|
// Wave type (square, sawtooth, sine, noise)
|
|
int waveTypeValue;
|
|
|
|
// Wave envelope parameters
|
|
float attackTimeValue;
|
|
float sustainTimeValue;
|
|
float sustainPunchValue;
|
|
float decayTimeValue;
|
|
|
|
// Frequency parameters
|
|
float startFrequencyValue;
|
|
float minFrequencyValue;
|
|
float slideValue;
|
|
float deltaSlideValue;
|
|
float vibratoDepthValue;
|
|
float vibratoSpeedValue;
|
|
//float vibratoPhaseDelayValue;
|
|
|
|
// Tone change parameters
|
|
float changeAmountValue;
|
|
float changeSpeedValue;
|
|
|
|
// Square wave parameters
|
|
float squareDutyValue;
|
|
float dutySweepValue;
|
|
|
|
// Repeat parameters
|
|
float repeatSpeedValue;
|
|
|
|
// Phaser parameters
|
|
float phaserOffsetValue;
|
|
float phaserSweepValue;
|
|
|
|
// Filter parameters
|
|
float lpfCutoffValue;
|
|
float lpfCutoffSweepValue;
|
|
float lpfResonanceValue;
|
|
float hpfCutoffValue;
|
|
float hpfCutoffSweepValue;
|
|
//bool filterOn;
|
|
|
|
} WaveParams;
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Global Variables Definition
|
|
//----------------------------------------------------------------------------------
|
|
|
|
// Volume parameters
|
|
static float volumeValue = 0.6f; // Volume
|
|
const float masterVolume = 0.15f; // Master volume
|
|
|
|
// Export WAV variables
|
|
static int wavBitrate = 16; // Wave bitrate (sample size in bits)
|
|
static int wavFrequency = 44100; // Wave frequency (sample rate)
|
|
|
|
// Wave and Sound variables
|
|
static Wave wave; // Stores wave data
|
|
static WaveParams params; // Stores wave parameters for generation
|
|
static Sound sound; // Stores sound data (OpenAL)
|
|
|
|
static char currentPath[256]; // Path to current working folder
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Module Functions Declaration
|
|
//----------------------------------------------------------------------------------
|
|
static void ResetParams(void); // Reset sound parameters
|
|
static void GenerateWave(WaveParams params); // Generate wave data and update sound
|
|
static void GeneratePlay(void); // Generate wave data and play sound
|
|
|
|
static void LoadSoundParams(const char *fileName); // Load sound parameters from file
|
|
static void SaveSoundParams(const char *fileName); // Save sound parameters to file
|
|
static void SaveWAV(const char *fileName, Wave wave); // Export sound to .wav file
|
|
static void DrawWave(Rectangle bounds, Color color); // Draw wave data using lines
|
|
static const char *GetExtension(const char *fileName); // Get extension from filename
|
|
|
|
// Buttons functions
|
|
static void BtnPickupCoin(void); // Generate sound: Pickup/Coin
|
|
static void BtnLaserShoot(void); // Generate sound: Laser shoot
|
|
static void BtnExplosion(void); // Generate sound: Explosion
|
|
static void BtnPowerup(void); // Generate sound: Powerup
|
|
static void BtnHitHurt(void); // Generate sound: Hit/Hurt
|
|
static void BtnJump(void); // Generate sound: Jump
|
|
static void BtnBlipSelect(void); // Generate sound: Blip/Select
|
|
static void BtnRandomize(void); // Generate random sound
|
|
static void BtnMutate(void);; // Mutate current sound
|
|
|
|
static void BtnPlaySound(void); // Play current sound
|
|
static void BtnLoadSound(void); // Load sound parameters file
|
|
static void BtnSaveSound(void); // Save sound parameters file
|
|
static void BtnExportWav(void); // Export current sound as .wav
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Program main entry point
|
|
//------------------------------------------------------------------------------------
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// Command line utility to generate wav files directly from .sfs and .rfx
|
|
// NOTE: .sfs uses defaults: sampleRate = 22050, sampleSize = 8, channels = 1;
|
|
// .rfx files contain sampleRate, sampleSize and channels information
|
|
if (argc > 1)
|
|
{
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if ((strcmp(GetExtension(argv[i]), "rfx") == 0) ||
|
|
(strcmp(GetExtension(argv[i]), "sfs") == 0))
|
|
{
|
|
LoadSoundParams(argv[i]);
|
|
GenerateWave(params);
|
|
|
|
argv[i][strlen(argv[i]) - 3] = 'w';
|
|
argv[i][strlen(argv[i]) - 2] = 'a';
|
|
argv[i][strlen(argv[i]) - 1] = 'v';
|
|
|
|
//printf("output name: %s\n", argv[i]);
|
|
|
|
SaveWAV(argv[i], wave);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Initialization
|
|
//----------------------------------------------------------------------------------------
|
|
int screenWidth = 500;
|
|
int screenHeight = 500;
|
|
|
|
//SetConfigFlags(FLAG_MSAA_4X_HINT);
|
|
InitWindow(screenWidth, screenHeight, "rFXGen - raylib FX Sound Generator");
|
|
|
|
InitAudioDevice();
|
|
|
|
Rectangle paramsRec = { 117, 43, 265, 373 };
|
|
|
|
Image mask;
|
|
mask.width = 16;
|
|
mask.height = 16;
|
|
mask.mipmaps = 1;
|
|
mask.format = UNCOMPRESSED_GRAYSCALE;
|
|
mask.data = imTwitter;
|
|
|
|
Color *pixels = (Color *)malloc(16*16*sizeof(Color));
|
|
for (int i = 0; i < 16*16; i++) pixels[i] = WHITE;
|
|
Image twitter = LoadImageEx(pixels, 16, 16);
|
|
free(pixels);
|
|
|
|
ImageAlphaMask(&twitter, mask); // Add alpha mask to image
|
|
ImageFormat(&twitter, UNCOMPRESSED_GRAY_ALPHA);
|
|
|
|
Texture2D texTwitter = LoadTextureFromImage(twitter);
|
|
UnloadImage(twitter);
|
|
|
|
// Label data
|
|
//----------------------------------------------------------------------------------------
|
|
Rectangle lblAttackTimeRec = { paramsRec.x + 33, paramsRec.y + 5, 100, 10 };
|
|
Rectangle lblSustainTimeRec = { paramsRec.x + 31, paramsRec.y + 20, 100, 10 };
|
|
Rectangle lblSustainPunchRec = { paramsRec.x + 27, paramsRec.y + 35, 100, 10 };
|
|
Rectangle lblDecayTimeRec = { paramsRec.x + 37, paramsRec.y + 50, 100, 10 };
|
|
|
|
Rectangle lblStartFrequencyRec = { paramsRec.x + 7, paramsRec.y + 66 + 5, 100, 10 };
|
|
Rectangle lblMinFrequencyRec = { paramsRec.x + 27, paramsRec.y + 66 + 20, 100, 10 };
|
|
Rectangle lblSlideRec = { paramsRec.x + 55, paramsRec.y + 66 + 35, 100, 10 };
|
|
Rectangle lblDeltaSlideRec = { paramsRec.x + 35, paramsRec.y + 66 + 50, 100, 10 };
|
|
Rectangle lblVibratoDepthRec = { paramsRec.x + 26, paramsRec.y + 66 + 65, 100, 10 };
|
|
Rectangle lblVibratoSpeedRec = { paramsRec.x + 27, paramsRec.y + 66 + 80, 100, 10 };
|
|
|
|
Rectangle lblChangeAmountRec = { paramsRec.x + 25, paramsRec.y + 162 + 5, 100, 10 };
|
|
Rectangle lblChangeSpeedRec = { paramsRec.x + 30, paramsRec.y + 162 + 20, 100, 10 };
|
|
|
|
Rectangle lblSquareDutyRec = { paramsRec.x + 33, paramsRec.y + 198 + 5, 100, 10 };
|
|
Rectangle lblDutySweepRec = { paramsRec.x + 36, paramsRec.y + 198 + 20, 100, 10 };
|
|
|
|
Rectangle lblRepeatSpeedRec = { paramsRec.x + 29, paramsRec.y + 234 + 5, 100, 10 };
|
|
|
|
Rectangle lblPhaserOffsetRec = { paramsRec.x + 26, paramsRec.y + 255 + 5, 100, 10 };
|
|
Rectangle lblPhaserSweepRec = { paramsRec.x + 29, paramsRec.y + 255 + 20, 100, 10 };
|
|
|
|
Rectangle lblLpfCutoffRec = { paramsRec.x + 37, paramsRec.y + 291 + 5, 100, 10 };
|
|
Rectangle lblLpfCutoffSweepRec = { paramsRec.x + 4, paramsRec.y + 291 + 20, 100, 10 };
|
|
Rectangle lblLpfResonanceRec = { paramsRec.x + 27, paramsRec.y + 291 + 35, 100, 10 };
|
|
Rectangle lblHpfCutoffRec = { paramsRec.x + 36, paramsRec.y + 291 + 50, 100, 10 };
|
|
Rectangle lblHpfCutoffSweepRec = { paramsRec.x + 3, paramsRec.y + 291 + 65, 100, 10 };
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
// SliderBar data
|
|
//----------------------------------------------------------------------------------------
|
|
Rectangle sldrAttackTimeRec = { paramsRec.x + 127, paramsRec.y + 5, 100, 10 };
|
|
Rectangle sldrSustainTimeRec = { paramsRec.x + 127, paramsRec.y + 20, 100, 10 };
|
|
Rectangle sldrSustainPunchRec = { paramsRec.x + 127, paramsRec.y + 35, 100, 10 };
|
|
Rectangle sldrDecayTimeRec = { paramsRec.x + 127, paramsRec.y + 50, 100, 10 };
|
|
|
|
Rectangle sldrStartFrequencyRec = { paramsRec.x + 127, paramsRec.y + 66 + 5, 100, 10 };
|
|
Rectangle sldrMinFrequencyRec = { paramsRec.x + 127, paramsRec.y + 66 + 20, 100, 10 };
|
|
Rectangle sldrSlideRec = { paramsRec.x + 127, paramsRec.y + 66 + 35, 100, 10 };
|
|
Rectangle sldrDeltaSlideRec = { paramsRec.x + 127, paramsRec.y + 66 + 50, 100, 10 };
|
|
Rectangle sldrVibratoDepthRec = { paramsRec.x + 127, paramsRec.y + 66 + 65, 100, 10 };
|
|
Rectangle sldrVibratoSpeedRec = { paramsRec.x + 127, paramsRec.y + 66 + 80, 100, 10 };
|
|
|
|
Rectangle sldrChangeAmountRec = { paramsRec.x + 127, paramsRec.y + 162 + 5, 100, 10 };
|
|
Rectangle sldrChangeSpeedRec = { paramsRec.x + 127, paramsRec.y + 162 + 20, 100, 10 };
|
|
|
|
Rectangle sldrSquareDutyRec = { paramsRec.x + 127, paramsRec.y + 198 + 5, 100, 10 };
|
|
Rectangle sldrDutySweepRec = { paramsRec.x + 127, paramsRec.y + 198 + 20, 100, 10 };
|
|
|
|
Rectangle sldrRepeatSpeedRec = { paramsRec.x + 127, paramsRec.y + 234 + 5, 100, 10 };
|
|
|
|
Rectangle sldrPhaserOffsetRec = { paramsRec.x + 127, paramsRec.y + 255 + 5, 100, 10 };
|
|
Rectangle sldrPhaserSweepRec = { paramsRec.x + 127, paramsRec.y + 255 + 20, 100, 10 };
|
|
|
|
Rectangle sldrLpfCutoffRec = { paramsRec.x + 127, paramsRec.y + 291 + 5, 100, 10 };
|
|
Rectangle sldrLpfCutoffSweepRec = { paramsRec.x + 127, paramsRec.y + 291 + 20, 100, 10 };
|
|
Rectangle sldrLpfResonanceRec = { paramsRec.x + 127, paramsRec.y + 291 + 35, 100, 10 };
|
|
Rectangle sldrHpfCutoffRec = { paramsRec.x + 127, paramsRec.y + 291 + 50, 100, 10 };
|
|
Rectangle sldrHpfCutoffSweepRec = { paramsRec.x + 127, paramsRec.y + 291 + 65, 100, 10 };
|
|
|
|
Rectangle sldrVolumeRec = { 394, 65, 92, 10 };
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
// Button data
|
|
//----------------------------------------------------------------------------------------
|
|
Rectangle btnPickupCoinRec = { 13, 48, 92, 20 };
|
|
Rectangle btnLaserShootRec = { 13, 73, 92, 20 };
|
|
Rectangle btnExplosionRec = { 13, 98, 92, 20 };
|
|
Rectangle btnPowerupRec = { 13, 123, 92, 20 };
|
|
Rectangle btnHitHurtRec = { 13, 148, 92, 20 };
|
|
Rectangle btnJumpRec = { 13, 173, 92, 20 };
|
|
Rectangle btnBlipSelectRec = { 13, 198, 92, 20 };
|
|
Rectangle btnMutateRec = { 13, 364, 92, 20 };
|
|
Rectangle btnRandomizeRec = { 13, 389, 92, 20 };
|
|
|
|
Rectangle btnPlaySoundRec = { 394, 81, 92, 20 };
|
|
Rectangle btnLoadSoundRec = { 394, 283, 92, 20 };
|
|
Rectangle btnSaveSoundRec = { 394, 307, 92, 20 };
|
|
Rectangle btnExportWavRec = { 394, 389, 92, 20 };
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
// CheckBox data
|
|
//----------------------------------------------------------------------------------------
|
|
Rectangle chkboxPlayOnChangeRec = { 394, 115, 10, 10 };
|
|
bool playOnChangeValue = true;
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
// ComboBox data
|
|
//----------------------------------------------------------------------------------------
|
|
Rectangle comboxFrequencyRec = { 394, 340, 61, 20 };
|
|
Rectangle comboxBitRateRec = { 394, 364, 61, 20 };
|
|
char *comboxFrequencyText[2] = { "44100 Hz", "22050 Hz" };
|
|
char *comboxBitRateText[2] = { "16 bit", "8 bit" };
|
|
int comboxFrequencyValue = 1;
|
|
int comboxBitRateValue = 1;
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
// ToggleGroup data
|
|
//----------------------------------------------------------------------------------------
|
|
Rectangle tgroupWaveTypeRec = { 117, 15, 64, 20 };
|
|
char *tgroupWaveTypeText[4] = { "Square", "Sawtooth", "Sinewave", "Noise" };
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
// Wave default parameters
|
|
wave.sampleRate = 44100;
|
|
wave.sampleSize = 32; // 32 bit -> float
|
|
wave.channels = 1;
|
|
|
|
wave.sampleCount = MAX_WAVE_LENGTH*wave.sampleRate*wave.channels;
|
|
wave.data = (float *)malloc(wave.sampleCount*sizeof(float));
|
|
|
|
sound = LoadSoundFromWave(wave);
|
|
SetSoundVolume(sound, volumeValue);
|
|
|
|
ResetParams();
|
|
|
|
Rectangle waveRec = { 13, 421, 473, 50 };
|
|
|
|
#define RENDER_WAVE_TO_TEXTURE
|
|
#if defined(RENDER_WAVE_TO_TEXTURE)
|
|
// To avoid enabling MSXAAx4, we will render wave to a texture x2
|
|
RenderTexture2D waveTarget = LoadRenderTexture(waveRec.width*2, waveRec.height*2);
|
|
#endif
|
|
|
|
// Get current directory
|
|
// NOTE: Current working directory could not match current executable directory
|
|
GetCurrentDir(currentPath, sizeof(currentPath));
|
|
currentPath[strlen(currentPath)] = '\\';
|
|
currentPath[strlen(currentPath) + 1] = '\0'; // Not really required
|
|
|
|
SetTargetFPS(60);
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
// Main game loop
|
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
|
{
|
|
// Update & Draw
|
|
//------------------------------------------------------------------------------------
|
|
if (IsKeyPressed(KEY_SPACE)) PlaySound(sound);
|
|
|
|
// Check if mouse is moving sliders to generate new wave data when mouse released
|
|
if ((CheckCollisionPointRec(GetMousePosition(), (Rectangle){ 265, 65, 105, 360 })) && (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)))
|
|
{
|
|
if (playOnChangeValue) GeneratePlay();
|
|
else GenerateWave(params);
|
|
}
|
|
|
|
BeginDrawing();
|
|
|
|
ClearBackground(GuiBackgroundColor());
|
|
|
|
DrawRectangleLines(paramsRec.x, paramsRec.y - 1, paramsRec.width, paramsRec.height + 1, GuiLinesColor());
|
|
|
|
DrawLine(paramsRec.x, paramsRec.y + 66, paramsRec.x + paramsRec.width, paramsRec.y + 66, GuiLinesColor());
|
|
DrawLine(paramsRec.x, paramsRec.y + 66 + 96, paramsRec.x + paramsRec.width, paramsRec.y + 66 + 96, GuiLinesColor());
|
|
DrawLine(paramsRec.x, paramsRec.y + 162 + 36, paramsRec.x + paramsRec.width, paramsRec.y + 162 + 36, GuiLinesColor());
|
|
DrawLine(paramsRec.x, paramsRec.y + 198 + 36, paramsRec.x + paramsRec.width, paramsRec.y + 198 + 36, GuiLinesColor());
|
|
DrawLine(paramsRec.x, paramsRec.y + 234 + 21, paramsRec.x + paramsRec.width, paramsRec.y + 234 + 21, GuiLinesColor());
|
|
DrawLine(paramsRec.x, paramsRec.y + 291, paramsRec.x + paramsRec.width, paramsRec.y + 291, GuiLinesColor());
|
|
|
|
DrawLine(13, 225, 105, 224, GuiLinesColor());
|
|
DrawLine(13, 358, 105, 358, GuiLinesColor());
|
|
DrawLine(394, 108, 486, 108, GuiLinesColor());
|
|
DrawLine(394, 277, 486, 277, GuiLinesColor());
|
|
DrawLine(394, 334, 486, 334, GuiLinesColor());
|
|
|
|
// Labels
|
|
//--------------------------------------------------------------------------------
|
|
DrawText("rFXGen", 28, 19, 20, DARKGRAY);
|
|
|
|
GuiLabel(lblAttackTimeRec, "ATTACK TIME");
|
|
GuiLabel(lblSustainTimeRec, "SUSTAIN TIME");
|
|
GuiLabel(lblSustainPunchRec, "SUSTAIN PUNCH");
|
|
GuiLabel(lblDecayTimeRec, "DECAY TIME");
|
|
// ---------
|
|
GuiLabel(lblStartFrequencyRec, "START FREQUENCY");
|
|
GuiLabel(lblMinFrequencyRec, "MIN FREQUENCY");
|
|
GuiLabel(lblSlideRec, "SLIDE");
|
|
GuiLabel(lblDeltaSlideRec, "DELTA SLIDE");
|
|
GuiLabel(lblVibratoDepthRec, "VIBRATO DEPTH");
|
|
GuiLabel(lblVibratoSpeedRec, "VIBRATO SPEED");
|
|
// ---------
|
|
GuiLabel(lblChangeAmountRec, "CHANGE AMOUNT");
|
|
GuiLabel(lblChangeSpeedRec, "CHANGE SPEED");
|
|
// ---------
|
|
GuiLabel(lblSquareDutyRec, "SQUARE DUTY");
|
|
GuiLabel(lblDutySweepRec, "DUTY SWEEP");
|
|
// ---------
|
|
GuiLabel(lblRepeatSpeedRec, "REPEAT SPEED");
|
|
// ---------
|
|
GuiLabel(lblPhaserOffsetRec, "PHASER OFFSET");
|
|
GuiLabel(lblPhaserSweepRec, "PHASER SWEEP");
|
|
// ---------
|
|
GuiLabel(lblLpfCutoffRec, "LPF CUTOFF");
|
|
GuiLabel(lblLpfCutoffSweepRec, "LPF CUTOFF SWEEP");
|
|
GuiLabel(lblLpfResonanceRec, "LPF RESONANCE");
|
|
GuiLabel(lblHpfCutoffRec, "HPF CUTOFF");
|
|
GuiLabel(lblHpfCutoffSweepRec, "HPF CUTOFF SWEEP");
|
|
//--------------------------------------------------------------------------------
|
|
|
|
// Sliders
|
|
//--------------------------------------------------------------------------------
|
|
params.attackTimeValue = GuiSliderBar(sldrAttackTimeRec, params.attackTimeValue, 0, 1);
|
|
params.sustainTimeValue = GuiSliderBar(sldrSustainTimeRec, params.sustainTimeValue, 0, 1);
|
|
params.sustainPunchValue = GuiSliderBar(sldrSustainPunchRec, params.sustainPunchValue, 0, 1);
|
|
params.decayTimeValue = GuiSliderBar(sldrDecayTimeRec, params.decayTimeValue, 0, 1);
|
|
// --------------
|
|
params.startFrequencyValue = GuiSliderBar(sldrStartFrequencyRec, params.startFrequencyValue, 0, 1);
|
|
params.minFrequencyValue = GuiSliderBar(sldrMinFrequencyRec, params.minFrequencyValue, 0, 1);
|
|
params.slideValue = GuiSliderBar(sldrSlideRec, params.slideValue, -1, 1);
|
|
params.deltaSlideValue = GuiSliderBar(sldrDeltaSlideRec, params.deltaSlideValue, -1, 1);
|
|
params.vibratoDepthValue = GuiSliderBar(sldrVibratoDepthRec, params.vibratoDepthValue, 0, 1);
|
|
params.vibratoSpeedValue = GuiSliderBar(sldrVibratoSpeedRec, params.vibratoSpeedValue, 0, 1);
|
|
// --------------
|
|
params.changeAmountValue = GuiSliderBar(sldrChangeAmountRec, params.changeAmountValue, -1, 1);
|
|
params.changeSpeedValue = GuiSliderBar(sldrChangeSpeedRec, params.changeSpeedValue, 0, 1);
|
|
// --------------
|
|
params.squareDutyValue = GuiSliderBar(sldrSquareDutyRec, params.squareDutyValue, 0, 1);
|
|
params.dutySweepValue = GuiSliderBar(sldrDutySweepRec, params.dutySweepValue, -1, 1);
|
|
// --------------
|
|
params.repeatSpeedValue = GuiSliderBar(sldrRepeatSpeedRec, params.repeatSpeedValue, 0, 1);
|
|
// ---------------
|
|
params.phaserOffsetValue = GuiSliderBar(sldrPhaserOffsetRec, params.phaserOffsetValue, -1, 1);
|
|
params.phaserSweepValue = GuiSliderBar(sldrPhaserSweepRec, params.phaserSweepValue, -1, 1);
|
|
// ---------------
|
|
params.lpfCutoffValue = GuiSliderBar(sldrLpfCutoffRec, params.lpfCutoffValue, 0, 1);
|
|
params.lpfCutoffSweepValue = GuiSliderBar(sldrLpfCutoffSweepRec, params.lpfCutoffSweepValue, -1, 1);
|
|
params.lpfResonanceValue = GuiSliderBar(sldrLpfResonanceRec, params.lpfResonanceValue, 0, 1);
|
|
params.hpfCutoffValue = GuiSliderBar(sldrHpfCutoffRec, params.hpfCutoffValue, 0, 1);
|
|
params.hpfCutoffSweepValue = GuiSliderBar(sldrHpfCutoffSweepRec, params.hpfCutoffSweepValue, -1, 1);
|
|
// ----------------
|
|
float previousVolumeValue = volumeValue;
|
|
volumeValue = GuiSliderBar(sldrVolumeRec, volumeValue, 0, 1);
|
|
if (volumeValue != previousVolumeValue) SetSoundVolume(sound, volumeValue);
|
|
//--------------------------------------------------------------------------------
|
|
|
|
// Slider values
|
|
//--------------------------------------------------------------------------------
|
|
DrawText(FormatText("%.02f", params.attackTimeValue), sldrAttackTimeRec.x + sldrAttackTimeRec.width + 7, sldrAttackTimeRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.sustainTimeValue), sldrSustainTimeRec.x + sldrSustainTimeRec.width + 7, sldrSustainTimeRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.sustainPunchValue), sldrSustainPunchRec.x + sldrSustainPunchRec.width + 7, sldrSustainPunchRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.decayTimeValue), sldrDecayTimeRec.x + sldrDecayTimeRec.width + 7, sldrDecayTimeRec.y + 1, 10, DARKGRAY);
|
|
// ---------------
|
|
DrawText(FormatText("%.02f", params.startFrequencyValue), sldrStartFrequencyRec.x + sldrStartFrequencyRec.width + 7, sldrStartFrequencyRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.minFrequencyValue), sldrMinFrequencyRec.x + sldrMinFrequencyRec.width + 7, sldrMinFrequencyRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.slideValue), sldrSlideRec.x + sldrSlideRec.width + 7, sldrSlideRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.deltaSlideValue), sldrDeltaSlideRec.x + sldrDeltaSlideRec.width + 7, sldrDeltaSlideRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.vibratoDepthValue), sldrVibratoDepthRec.x + sldrVibratoDepthRec.width + 7, sldrVibratoDepthRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.vibratoSpeedValue), sldrVibratoSpeedRec.x + sldrVibratoSpeedRec.width + 7, sldrVibratoSpeedRec.y + 1, 10, DARKGRAY);
|
|
// ---------------
|
|
DrawText(FormatText("%.02f", params.changeAmountValue), sldrChangeAmountRec.x + sldrChangeAmountRec.width + 7, sldrChangeAmountRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.changeSpeedValue), sldrChangeSpeedRec.x + sldrChangeSpeedRec.width + 7, sldrChangeSpeedRec.y + 1, 10, DARKGRAY);
|
|
// ---------------
|
|
DrawText(FormatText("%.02f", params.squareDutyValue), sldrSquareDutyRec.x + sldrSquareDutyRec.width + 7, sldrSquareDutyRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.dutySweepValue), sldrDutySweepRec.x + sldrDutySweepRec.width + 7, sldrDutySweepRec.y + 1, 10, DARKGRAY);
|
|
// ---------------
|
|
DrawText(FormatText("%.02f", params.repeatSpeedValue), sldrRepeatSpeedRec.x + sldrRepeatSpeedRec.width + 7, sldrRepeatSpeedRec.y + 1, 10, DARKGRAY);
|
|
// ---------------
|
|
DrawText(FormatText("%.02f", params.phaserOffsetValue), sldrPhaserOffsetRec.x + sldrPhaserOffsetRec.width + 7, sldrPhaserOffsetRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.phaserSweepValue), sldrPhaserSweepRec.x + sldrPhaserSweepRec.width + 7, sldrPhaserSweepRec.y + 1, 10, DARKGRAY);
|
|
// ---------------
|
|
DrawText(FormatText("%.02f", params.lpfCutoffValue), sldrLpfCutoffRec.x + sldrLpfCutoffRec.width + 7, sldrLpfCutoffRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.lpfCutoffSweepValue), sldrLpfCutoffSweepRec.x + sldrLpfCutoffSweepRec.width + 7, sldrLpfCutoffSweepRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.lpfResonanceValue), sldrLpfResonanceRec.x + sldrLpfResonanceRec.width + 7, sldrLpfResonanceRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.hpfCutoffValue), sldrHpfCutoffRec.x + sldrHpfCutoffRec.width + 7, sldrHpfCutoffRec.y + 1, 10, DARKGRAY);
|
|
DrawText(FormatText("%.02f", params.hpfCutoffSweepValue), sldrHpfCutoffSweepRec.x + sldrHpfCutoffSweepRec.width + 7, sldrHpfCutoffSweepRec.y + 1, 10, DARKGRAY);
|
|
//--------------------------------------------------------------------------------
|
|
|
|
// Buttons
|
|
//--------------------------------------------------------------------------------
|
|
if (GuiButton(btnPickupCoinRec, "Pickup/Coin")) BtnPickupCoin();
|
|
if (GuiButton(btnLaserShootRec, "Laser/Shoot")) BtnLaserShoot();
|
|
if (GuiButton(btnExplosionRec, "Explosion")) BtnExplosion();
|
|
if (GuiButton(btnPowerupRec, "Powerup")) BtnPowerup();
|
|
if (GuiButton(btnHitHurtRec, "Hit/Hurt")) BtnHitHurt();
|
|
if (GuiButton(btnJumpRec, "Jump")) BtnJump();
|
|
if (GuiButton(btnBlipSelectRec, "Blip/Select")) BtnBlipSelect();
|
|
if (GuiButton(btnMutateRec, "Mutate")) BtnMutate();
|
|
if (GuiButton(btnRandomizeRec, "Randomize")) BtnRandomize();
|
|
if (GuiButton(btnPlaySoundRec, "Play Sound")) BtnPlaySound();
|
|
if (GuiButton(btnLoadSoundRec, "Load Sound")) BtnLoadSound();
|
|
if (GuiButton(btnSaveSoundRec, "Save Sound")) BtnSaveSound();
|
|
if (GuiButton(btnExportWavRec, "Export .Wav")) BtnExportWav();
|
|
//--------------------------------------------------------------------------------
|
|
|
|
// CheckBox
|
|
//--------------------------------------------------------------------------------
|
|
playOnChangeValue = GuiCheckBox(chkboxPlayOnChangeRec, " Play on change", playOnChangeValue);
|
|
//--------------------------------------------------------------------------------
|
|
|
|
// ComboBox
|
|
//--------------------------------------------------------------------------------
|
|
comboxFrequencyValue = GuiComboBox(comboxFrequencyRec, 2, comboxFrequencyText, comboxFrequencyValue);
|
|
comboxBitRateValue = GuiComboBox(comboxBitRateRec, 2, comboxBitRateText, comboxBitRateValue);
|
|
|
|
if (comboxFrequencyValue == 0) wavFrequency = 44100;
|
|
else if (comboxFrequencyValue == 1) wavFrequency = 22050;
|
|
|
|
if (comboxBitRateValue == 0) wavBitrate = 16;
|
|
else if (comboxBitRateValue == 1) wavBitrate = 8;
|
|
//--------------------------------------------------------------------------------
|
|
|
|
// ToggleGroup
|
|
//--------------------------------------------------------------------------------
|
|
int previousWaveTypeValue = params.waveTypeValue;
|
|
params.waveTypeValue = GuiToggleGroup(tgroupWaveTypeRec, 4, tgroupWaveTypeText, params.waveTypeValue);
|
|
if (params.waveTypeValue != previousWaveTypeValue) GeneratePlay();
|
|
//--------------------------------------------------------------------------------
|
|
|
|
if (volumeValue < 1.0f) DrawText(FormatText("VOLUME: %02i %%", (int)(volumeValue*100.0f)), 394, 49, 10, DARKGRAY);
|
|
else DrawText(FormatText("VOLUME: %02i %%", (int)(volumeValue*100.0f)), 394, 49, 10, DARKGRAY);
|
|
|
|
#if defined(RENDER_WAVE_TO_TEXTURE)
|
|
BeginTextureMode(waveTarget);
|
|
DrawWave((Rectangle){ 0, 0, waveTarget.texture.width, waveTarget.texture.height }, MAROON);
|
|
EndTextureMode();
|
|
|
|
DrawTextureEx(waveTarget.texture, (Vector2){ waveRec.x, waveRec.y }, 0.0f, 0.5f, WHITE);
|
|
#else
|
|
DrawWave(waveRec, MAROON);
|
|
#endif
|
|
DrawRectangleLines(waveRec.x, waveRec.y, waveRec.width, waveRec.height, GuiLinesColor());
|
|
DrawRectangle(waveRec.x, waveRec.y + waveRec.height/2, waveRec.width, 1, LIGHTGRAY);
|
|
|
|
// Draw status bar
|
|
DrawRectangle(0, screenHeight - 20, screenWidth, 20, Fade(LIGHTGRAY, 0.5f));
|
|
DrawLine(0, screenHeight - 20, screenWidth, screenHeight - 20, LIGHTGRAY);
|
|
DrawText("SOUND INFO:", 28, 486, 10, DARKGRAY);
|
|
DrawText(FormatText("num samples: %i", wave.sampleCount), 117, 486, 10, DARKGRAY);
|
|
DrawText(FormatText("| duration: %i ms", wave.sampleCount*1000/(wave.sampleRate*wave.channels)), 234, 486, 10, DARKGRAY);
|
|
DrawText(FormatText("| Wave size: %i bytes", wave.sampleCount*wavBitrate/8), 355, 486, 10, DARKGRAY);
|
|
|
|
// Adverts
|
|
//DrawText("based on sfxr by\nTomas Pettersson", 18, 240, 10, GRAY);
|
|
DrawText("based on sfxr by", 16, 235, 10, GRAY);
|
|
DrawText("Tomas Pettersson", 13, 248, 10, GRAY);
|
|
|
|
DrawLine(13, 268, 105, 268, GuiLinesColor());
|
|
|
|
DrawText("www/github.com/\nraysan5/raygui", 18, 280, 10, GRAY);
|
|
DrawText("www/github.com/\nraysan5/raylib", 18, 318, 10, GRAY);
|
|
DrawText("powered by", 394, 149, 10, DARKGRAY);
|
|
DrawRectangle(394, 162, 92, 92, BLACK);
|
|
DrawRectangle(400, 168, 80, 80, RAYWHITE);
|
|
DrawText("raylib", 419, 223, 20, BLACK);
|
|
|
|
//DrawText("based on sfxr by", 394, 13, 10, GRAY);
|
|
//DrawText("Tomas Pettersson", 392, 25, 10, GRAY);
|
|
|
|
DrawText("@raysan5", 421, 21, 10, GRAY);
|
|
DrawTexture(texTwitter, 400, 18, Fade(BLACK, 0.4f));
|
|
|
|
//DrawText("@raysan5", 40, 242, 10, GRAY);
|
|
//DrawTexture(texTwitter, 20, 240, Fade(BLACK, 0.4f));
|
|
|
|
EndDrawing();
|
|
//------------------------------------------------------------------------------------
|
|
|
|
//if (doPlay) PlaySample();
|
|
}
|
|
|
|
// De-Initialization
|
|
//----------------------------------------------------------------------------------------
|
|
UnloadSound(sound);
|
|
UnloadWave(wave);
|
|
|
|
#if defined(RENDER_WAVE_TO_TEXTURE)
|
|
UnloadRenderTexture(waveTarget);
|
|
#endif
|
|
|
|
UnloadTexture(texTwitter);
|
|
|
|
CloseAudioDevice();
|
|
|
|
CloseWindow(); // Close window and OpenGL context
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Module Functions Definitions (local)
|
|
//--------------------------------------------------------------------------------------------
|
|
static void ResetParams()
|
|
{
|
|
// Wave type
|
|
params.waveTypeValue = 0;
|
|
|
|
// Wave envelope params
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = 0.3f;
|
|
params.sustainPunchValue = 0.0f;
|
|
params.decayTimeValue = 0.4f;
|
|
|
|
// Frequency params
|
|
params.startFrequencyValue = 0.3f;
|
|
params.minFrequencyValue = 0.0f;
|
|
params.slideValue = 0.0f;
|
|
params.deltaSlideValue = 0.0f;
|
|
params.vibratoDepthValue = 0.0f;
|
|
params.vibratoSpeedValue = 0.0f;
|
|
//params.vibratoPhaseDelay = 0.0f;
|
|
|
|
// Tone change params
|
|
params.changeAmountValue = 0.0f;
|
|
params.changeSpeedValue = 0.0f;
|
|
|
|
// Square wave params
|
|
params.squareDutyValue = 0.0f;
|
|
params.dutySweepValue = 0.0f;
|
|
|
|
// Repeat params
|
|
params.repeatSpeedValue = 0.0f;
|
|
|
|
// Phaser params
|
|
params.phaserOffsetValue = 0.0f;
|
|
params.phaserSweepValue = 0.0f;
|
|
|
|
// Filter params
|
|
params.lpfCutoffValue = 1.0f;
|
|
params.lpfCutoffSweepValue = 0.0f;
|
|
params.lpfResonanceValue = 0.0f;
|
|
params.hpfCutoffValue = 0.0f;
|
|
params.hpfCutoffSweepValue = 0.0f;
|
|
//params.filterOn = false;
|
|
}
|
|
|
|
// Generates new wave data and updates sound buffer
|
|
// NOTE: Operates on global variables: sound, wave
|
|
static void GenerateWave(WaveParams params)
|
|
{
|
|
// Configuration parameters for generation
|
|
// NOTE: Those parameters are calculated from selected values
|
|
int phase;
|
|
double fperiod;
|
|
double fmaxperiod;
|
|
double fslide;
|
|
double fdslide;
|
|
int period;
|
|
float squareDuty;
|
|
float squareSlide;
|
|
int envelopeStage;
|
|
int envelopeTime;
|
|
int envelopeLength[3];
|
|
float envelopeVolume;
|
|
float fphase;
|
|
float fdphase;
|
|
int iphase;
|
|
float phaserBuffer[1024];
|
|
int ipp;
|
|
float noiseBuffer[32];
|
|
float fltp;
|
|
float fltdp;
|
|
float fltw;
|
|
float fltwd;
|
|
float fltdmp;
|
|
float fltphp;
|
|
float flthp;
|
|
float flthpd;
|
|
float vibratoPhase;
|
|
float vibratoSpeed;
|
|
float vibratoAmplitude;
|
|
int repeatTime;
|
|
int repeatLimit;
|
|
int arpeggioTime;
|
|
int arpeggioLimit;
|
|
double arpeggioModulation;
|
|
|
|
// Reset sample parameters
|
|
//----------------------------------------------------------------------------------------
|
|
phase = 0;
|
|
|
|
fperiod = 100.0/(params.startFrequencyValue*params.startFrequencyValue + 0.001);
|
|
period = (int)fperiod;
|
|
fmaxperiod = 100.0/(params.minFrequencyValue*params.minFrequencyValue + 0.001);
|
|
fslide = 1.0 - pow((double)params.slideValue, 3.0)*0.01;
|
|
fdslide = -pow((double)params.deltaSlideValue, 3.0)*0.000001;
|
|
squareDuty = 0.5f - params.squareDutyValue*0.5f;
|
|
squareSlide = -params.dutySweepValue*0.00005f;
|
|
|
|
if (params.changeAmountValue >= 0.0f) arpeggioModulation = 1.0 - pow((double)params.changeAmountValue, 2.0)*0.9;
|
|
else arpeggioModulation = 1.0 + pow((double)params.changeAmountValue, 2.0)*10.0;
|
|
|
|
arpeggioTime = 0;
|
|
arpeggioLimit = (int)(pow(1.0f - params.changeSpeedValue, 2.0f)*20000 + 32);
|
|
|
|
if (params.changeSpeedValue == 1.0f) arpeggioLimit = 0;
|
|
|
|
// Reset filter parameters
|
|
fltp = 0.0f;
|
|
fltdp = 0.0f;
|
|
fltw = pow(params.lpfCutoffValue, 3.0f)*0.1f;
|
|
fltwd = 1.0f + params.lpfCutoffSweepValue*0.0001f;
|
|
fltdmp = 5.0f/(1.0f + pow(params.lpfResonanceValue, 2.0f)*20.0f)*(0.01f + fltw);
|
|
if (fltdmp > 0.8f) fltdmp = 0.8f;
|
|
fltphp = 0.0f;
|
|
flthp = pow(params.hpfCutoffValue, 2.0f)*0.1f;
|
|
flthpd = 1.0 + params.hpfCutoffSweepValue*0.0003f;
|
|
|
|
// Reset vibrato
|
|
vibratoPhase = 0.0f;
|
|
vibratoSpeed = pow(params.vibratoSpeedValue, 2.0f)*0.01f;
|
|
vibratoAmplitude = params.vibratoDepthValue*0.5f;
|
|
|
|
// Reset envelope
|
|
envelopeVolume = 0.0f;
|
|
envelopeStage = 0;
|
|
envelopeTime = 0;
|
|
envelopeLength[0] = (int)(params.attackTimeValue*params.attackTimeValue*100000.0f);
|
|
envelopeLength[1] = (int)(params.sustainTimeValue*params.sustainTimeValue*100000.0f);
|
|
envelopeLength[2] = (int)(params.decayTimeValue*params.decayTimeValue*100000.0f);
|
|
|
|
fphase = pow(params.phaserOffsetValue, 2.0f)*1020.0f;
|
|
if (params.phaserOffsetValue < 0.0f) fphase = -fphase;
|
|
|
|
fdphase = pow(params.phaserSweepValue, 2.0f)*1.0f;
|
|
if (params.phaserSweepValue < 0.0f) fdphase = -fdphase;
|
|
|
|
iphase = abs((int)fphase);
|
|
ipp = 0;
|
|
|
|
for (int i = 0; i < 1024; i++) phaserBuffer[i] = 0.0f;
|
|
for (int i = 0; i < 32; i++) noiseBuffer[i] = frnd(2.0f) - 1.0f; // TODO: Review frnd()
|
|
|
|
repeatTime = 0;
|
|
repeatLimit = (int)(pow(1.0f - params.repeatSpeedValue, 2.0f)*20000 + 32);
|
|
|
|
if (params.repeatSpeedValue == 0.0f) repeatLimit = 0;
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
bool generatingSample = true;
|
|
float *buffer = (float *)wave.data;
|
|
|
|
for (int i = 0; i < MAX_WAVE_LENGTH*wave.sampleRate; i++)
|
|
{
|
|
if (!generatingSample)
|
|
{
|
|
wave.sampleCount = i;
|
|
break;
|
|
}
|
|
|
|
// Generate sample using selected parameters
|
|
//------------------------------------------------------------------------------------
|
|
repeatTime++;
|
|
|
|
if (repeatLimit != 0 && repeatTime >= repeatLimit)
|
|
{
|
|
// Reset sample parameters (only some of them)
|
|
repeatTime = 0;
|
|
|
|
fperiod = 100.0/(params.startFrequencyValue*params.startFrequencyValue + 0.001);
|
|
period = (int)fperiod;
|
|
fmaxperiod = 100.0/(params.minFrequencyValue*params.minFrequencyValue + 0.001);
|
|
fslide = 1.0 - pow((double)params.slideValue, 3.0)*0.01;
|
|
fdslide = -pow((double)params.deltaSlideValue, 3.0)*0.000001;
|
|
squareDuty = 0.5f - params.squareDutyValue*0.5f;
|
|
squareSlide = -params.dutySweepValue*0.00005f;
|
|
|
|
if (params.changeAmountValue >= 0.0f) arpeggioModulation = 1.0 - pow((double)params.changeAmountValue, 2.0)*0.9;
|
|
else arpeggioModulation = 1.0 + pow((double)params.changeAmountValue, 2.0)*10.0;
|
|
|
|
arpeggioTime = 0;
|
|
arpeggioLimit = (int)(pow(1.0f - params.changeSpeedValue, 2.0f)*20000 + 32);
|
|
|
|
if (params.changeSpeedValue == 1.0f) arpeggioLimit = 0;
|
|
}
|
|
|
|
// Frequency envelopes/arpeggios
|
|
arpeggioTime++;
|
|
|
|
if (arpeggioLimit !=0 && arpeggioTime >= arpeggioLimit)
|
|
{
|
|
arpeggioLimit = 0;
|
|
fperiod *= arpeggioModulation;
|
|
}
|
|
|
|
fslide += fdslide;
|
|
fperiod *= fslide;
|
|
|
|
if (fperiod > fmaxperiod)
|
|
{
|
|
fperiod = fmaxperiod;
|
|
|
|
if (params.minFrequencyValue > 0.0f) generatingSample = false; // TODO: minFrequencyValue shouldn't be here...
|
|
}
|
|
|
|
float rfperiod = fperiod;
|
|
|
|
if (vibratoAmplitude > 0.0f)
|
|
{
|
|
vibratoPhase += vibratoSpeed;
|
|
rfperiod = fperiod*(1.0 + sinf(vibratoPhase)*vibratoAmplitude);
|
|
}
|
|
|
|
period = (int)rfperiod;
|
|
|
|
if (period < 8) period=8;
|
|
|
|
squareDuty += squareSlide;
|
|
|
|
if (squareDuty < 0.0f) squareDuty = 0.0f;
|
|
if (squareDuty > 0.5f) squareDuty = 0.5f;
|
|
|
|
// Volume envelope
|
|
envelopeTime++;
|
|
|
|
if (envelopeTime>envelopeLength[envelopeStage])
|
|
{
|
|
envelopeTime = 0;
|
|
envelopeStage++;
|
|
|
|
if (envelopeStage == 3) generatingSample = false;
|
|
}
|
|
|
|
if (envelopeStage == 0) envelopeVolume = (float)envelopeTime/envelopeLength[0];
|
|
if (envelopeStage == 1) envelopeVolume = 1.0f + pow(1.0f - (float)envelopeTime/envelopeLength[1], 1.0f)*2.0f*params.sustainPunchValue;
|
|
if (envelopeStage == 2) envelopeVolume = 1.0f - (float)envelopeTime/envelopeLength[2];
|
|
|
|
// Phaser step
|
|
fphase += fdphase;
|
|
iphase = abs((int)fphase);
|
|
|
|
if (iphase > 1023) iphase = 1023;
|
|
|
|
if (flthpd != 0.0f)
|
|
{
|
|
flthp *= flthpd;
|
|
if (flthp < 0.00001f) flthp = 0.00001f;
|
|
if (flthp > 0.1f) flthp = 0.1f;
|
|
}
|
|
|
|
float ssample = 0.0f;
|
|
|
|
// Supersampling x8
|
|
for (int si = 0; si < 8; si++)
|
|
{
|
|
float sample = 0.0f;
|
|
phase++;
|
|
|
|
if (phase >= period)
|
|
{
|
|
//phase = 0;
|
|
phase %= period;
|
|
if (params.waveTypeValue == 3)
|
|
for (int i = 0;i < 32; i++)
|
|
noiseBuffer[i] = frnd(2.0f) - 1.0f; // TODO: Review frnd()
|
|
}
|
|
|
|
// base waveform
|
|
float fp = (float)phase/period;
|
|
|
|
switch (params.waveTypeValue)
|
|
{
|
|
case 0: // Square wave
|
|
{
|
|
if (fp < squareDuty) sample = 0.5f;
|
|
else sample = -0.5f;
|
|
|
|
} break;
|
|
case 1: // Sawtooth wave
|
|
{
|
|
sample = 1.0f - fp*2;
|
|
|
|
} break;
|
|
case 2: // Sine wave
|
|
{
|
|
sample = sinf(fp*2*PI);
|
|
|
|
} break;
|
|
case 3: // Noise wave
|
|
{
|
|
sample = noiseBuffer[phase*32/period];
|
|
|
|
} break;
|
|
default: break;
|
|
}
|
|
|
|
// LP filter
|
|
float pp = fltp;
|
|
fltw *= fltwd;
|
|
|
|
if (fltw < 0.0f) fltw = 0.0f;
|
|
if (fltw > 0.1f) fltw = 0.1f;
|
|
if (params.lpfCutoffValue != 1.0f)
|
|
{
|
|
fltdp += (sample-fltp)*fltw;
|
|
fltdp -= fltdp*fltdmp;
|
|
}
|
|
else
|
|
{
|
|
fltp = sample;
|
|
fltdp = 0.0f;
|
|
}
|
|
|
|
fltp += fltdp;
|
|
|
|
// HP filter
|
|
fltphp += fltp - pp;
|
|
fltphp -= fltphp*flthp;
|
|
sample = fltphp;
|
|
|
|
// Phaser
|
|
phaserBuffer[ipp&1023] = sample;
|
|
sample += phaserBuffer[(ipp - iphase + 1024) & 1023];
|
|
ipp = (ipp + 1) & 1023;
|
|
|
|
// Final accumulation and envelope application
|
|
ssample += sample*envelopeVolume;
|
|
}
|
|
|
|
ssample = ssample/8*masterVolume; // TODO: Why masterVolume?
|
|
ssample *= 2.0f*volumeValue;
|
|
//------------------------------------------------------------------------------------
|
|
|
|
// Accumulate samples in the buffer
|
|
if (wave.data != NULL)
|
|
{
|
|
if (ssample > 1.0f) ssample = 1.0f;
|
|
if (ssample < -1.0f) ssample = -1.0f;
|
|
|
|
*buffer++ = ssample;
|
|
}
|
|
}
|
|
|
|
// Update sound buffer with new data
|
|
UpdateSound(sound, wave.data, wave.sampleCount);
|
|
}
|
|
|
|
// Generate wave, update sound and play sound
|
|
static void GeneratePlay(void)
|
|
{
|
|
GenerateWave(params);
|
|
PlaySound(sound);
|
|
}
|
|
|
|
// Load .rfx (rFXGen) or .sfs (sfxr) sound parameters file
|
|
static void LoadSoundParams(const char* fileName)
|
|
{
|
|
if (strcmp(GetExtension(fileName),"sfs") == 0)
|
|
{
|
|
FILE *file = fopen(fileName, "rb");
|
|
|
|
// Load .sfs sound parameters
|
|
int version = 0;
|
|
fread(&version, 1, sizeof(int), file);
|
|
|
|
if ((version == 100) || (version == 101) || (version == 102))
|
|
{
|
|
fread(¶ms.waveTypeValue, 1, sizeof(int), file);
|
|
|
|
volumeValue = 0.5f;
|
|
|
|
if (version == 102) fread(&volumeValue, 1, sizeof(float), file);
|
|
|
|
fread(¶ms.startFrequencyValue, 1, sizeof(float), file);
|
|
fread(¶ms.minFrequencyValue, 1, sizeof(float), file);
|
|
fread(¶ms.slideValue, 1, sizeof(float), file);
|
|
|
|
if (version >= 101) fread(¶ms.deltaSlideValue, 1, sizeof(float), file);
|
|
|
|
fread(¶ms.squareDutyValue, 1, sizeof(float), file);
|
|
fread(¶ms.dutySweepValue, 1, sizeof(float), file);
|
|
|
|
fread(¶ms.vibratoDepthValue, 1, sizeof(float), file);
|
|
fread(¶ms.vibratoSpeedValue, 1, sizeof(float), file);
|
|
|
|
float vibratoPhaseDelay = 0.0f;
|
|
fread(&vibratoPhaseDelay, 1, sizeof(float), file); // Not used
|
|
|
|
fread(¶ms.attackTimeValue, 1, sizeof(float), file);
|
|
fread(¶ms.sustainTimeValue, 1, sizeof(float), file);
|
|
fread(¶ms.decayTimeValue, 1, sizeof(float), file);
|
|
fread(¶ms.sustainPunchValue, 1, sizeof(float), file);
|
|
|
|
bool filterOn = false;
|
|
fread(&filterOn, 1, sizeof(bool), file); // Not used
|
|
|
|
fread(¶ms.lpfResonanceValue, 1, sizeof(float), file);
|
|
fread(¶ms.lpfCutoffValue, 1, sizeof(float), file);
|
|
fread(¶ms.lpfCutoffSweepValue, 1, sizeof(float), file);
|
|
fread(¶ms.hpfCutoffValue, 1, sizeof(float), file);
|
|
fread(¶ms.hpfCutoffSweepValue, 1, sizeof(float), file);
|
|
|
|
fread(¶ms.phaserOffsetValue, 1, sizeof(float), file);
|
|
fread(¶ms.phaserSweepValue, 1, sizeof(float), file);
|
|
fread(¶ms.repeatSpeedValue, 1, sizeof(float), file);
|
|
|
|
if (version >= 101)
|
|
{
|
|
fread(¶ms.changeSpeedValue, 1, sizeof(float), file);
|
|
fread(¶ms.changeAmountValue, 1, sizeof(float), file);
|
|
}
|
|
}
|
|
else printf("SFS file version not supported\n");
|
|
|
|
fclose(file);
|
|
}
|
|
else if (strcmp(GetExtension(fileName),"rfx") == 0)
|
|
{
|
|
FILE *rfxFile = fopen(fileName, "rb");
|
|
|
|
// Load .rfx sound parameters
|
|
char signature[4];
|
|
fread(signature, 4, sizeof(char), rfxFile);
|
|
|
|
if ((signature[0] == 'r') &&
|
|
(signature[0] == 'F') &&
|
|
(signature[0] == 'X') &&
|
|
(signature[0] == ' '))
|
|
{
|
|
int version;
|
|
fread(&version, 1, sizeof(int), rfxFile);
|
|
|
|
fread(&volumeValue, 1, sizeof(float), rfxFile);
|
|
fread(&wavBitrate, 1, sizeof(int), rfxFile);
|
|
fread(&wavFrequency, 1, sizeof(int), rfxFile);
|
|
|
|
int channels;
|
|
fread(&channels, 1, sizeof(int), rfxFile);
|
|
|
|
// Read wave parameters struct
|
|
fread(¶ms, 1, sizeof(WaveParams), rfxFile);
|
|
}
|
|
|
|
fclose(rfxFile);
|
|
}
|
|
|
|
}
|
|
|
|
// Save .rfx (rFXGen) or .sfs (sfxr) sound parameters file
|
|
static void SaveSoundParams(const char* fileName)
|
|
{
|
|
if (strcmp(GetExtension(fileName),"sfs") == 0)
|
|
{
|
|
FILE *sfsFile = fopen(fileName, "wb");
|
|
|
|
// Save .sfs sound parameters
|
|
int version = 102;
|
|
fwrite(&version, 1, sizeof(int), sfsFile);
|
|
|
|
fwrite(¶ms.waveTypeValue, 1, sizeof(int), sfsFile);
|
|
|
|
fwrite(&volumeValue, 1, sizeof(float), sfsFile);
|
|
|
|
fwrite(¶ms.startFrequencyValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.minFrequencyValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.slideValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.deltaSlideValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.squareDutyValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.dutySweepValue, 1, sizeof(float), sfsFile);
|
|
|
|
fwrite(¶ms.vibratoDepthValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.vibratoSpeedValue, 1, sizeof(float), sfsFile);
|
|
|
|
float vibratoPhaseDelay = 0.0f;
|
|
fwrite(&vibratoPhaseDelay, 1, sizeof(float), sfsFile); // Not used
|
|
|
|
fwrite(¶ms.attackTimeValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.sustainTimeValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.decayTimeValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.sustainPunchValue, 1, sizeof(float), sfsFile);
|
|
|
|
bool filterOn = false;
|
|
fwrite(&filterOn, 1, sizeof(bool), sfsFile); // Not used
|
|
|
|
fwrite(¶ms.lpfResonanceValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.lpfCutoffValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.lpfCutoffSweepValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.hpfCutoffValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.hpfCutoffSweepValue, 1, sizeof(float), sfsFile);
|
|
|
|
fwrite(¶ms.phaserOffsetValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.phaserSweepValue, 1, sizeof(float), sfsFile);
|
|
|
|
fwrite(¶ms.repeatSpeedValue, 1, sizeof(float), sfsFile);
|
|
|
|
fwrite(¶ms.changeSpeedValue, 1, sizeof(float), sfsFile);
|
|
fwrite(¶ms.changeAmountValue, 1, sizeof(float), sfsFile);
|
|
|
|
fclose(sfsFile);
|
|
}
|
|
else if (strcmp(GetExtension(fileName),"rfx") == 0)
|
|
{
|
|
FILE *rfxFile = fopen(fileName, "wb");
|
|
|
|
// Save .rfx sound parameters
|
|
char signature[4] = "rFX ";
|
|
fwrite(signature, 4, sizeof(char), rfxFile);
|
|
|
|
int version = 100; // File version
|
|
fwrite(&version, 1, sizeof(int), rfxFile);
|
|
|
|
fwrite(&volumeValue, 1, sizeof(float), rfxFile);
|
|
fwrite(&wavBitrate, 1, sizeof(int), rfxFile);
|
|
fwrite(&wavFrequency, 1, sizeof(int), rfxFile);
|
|
|
|
int channels = 1;
|
|
fwrite(&channels, 1, sizeof(int), rfxFile);
|
|
|
|
fwrite(¶ms, 1, sizeof(WaveParams), rfxFile);
|
|
|
|
fclose(rfxFile);
|
|
}
|
|
}
|
|
|
|
// Draw wave data
|
|
// NOTE: For proper visualization, MSAA x4 is recommended, alternatively
|
|
// it should be rendered to a bigger texture and then scaled down with
|
|
// bilinear/trilinear texture filtering
|
|
static void DrawWave(Rectangle bounds, Color color)
|
|
{
|
|
float sample, sampleNext;
|
|
float currentSample = 0.0f;
|
|
float sampleIncrement = (float)wave.sampleCount/(float)(bounds.width*2);
|
|
float sampleScale = (float)bounds.height;
|
|
|
|
for (int i = 1; i < bounds.width*2 - 1; i++)
|
|
{
|
|
sample = ((float *)wave.data)[(int)currentSample]*sampleScale;
|
|
sampleNext = ((float *)wave.data)[(int)(currentSample + sampleIncrement)]*sampleScale;
|
|
|
|
if (sample > bounds.height/2) sample = bounds.height/2;
|
|
else if (sample < -bounds.height/2) sample = -bounds.height/2;
|
|
|
|
if (sampleNext > bounds.height/2) sampleNext = bounds.height/2;
|
|
else if (sampleNext < -bounds.height/2) sampleNext = -bounds.height/2;
|
|
|
|
DrawLineV((Vector2){ (float)bounds.x + (float)i/2.0f, (float)(bounds.y + bounds.height/2) + sample },
|
|
(Vector2){ (float)bounds.x + (float)i/2.0f, (float)(bounds.y + bounds.height/2) + sampleNext }, color);
|
|
|
|
currentSample += sampleIncrement;
|
|
}
|
|
}
|
|
|
|
// Save wave data to WAV file
|
|
static void SaveWAV(const char *fileName, Wave wave)
|
|
{
|
|
// Basic WAV headers structs
|
|
typedef struct {
|
|
char chunkID[4];
|
|
int chunkSize;
|
|
char format[4];
|
|
} RiffHeader;
|
|
|
|
typedef struct {
|
|
char subChunkID[4];
|
|
int subChunkSize;
|
|
short audioFormat;
|
|
short numChannels;
|
|
int sampleRate;
|
|
int byteRate;
|
|
short blockAlign;
|
|
short bitsPerSample;
|
|
} WaveFormat;
|
|
|
|
typedef struct {
|
|
char subChunkID[4];
|
|
int subChunkSize;
|
|
} WaveData;
|
|
|
|
RiffHeader riffHeader;
|
|
WaveFormat waveFormat;
|
|
WaveData waveData;
|
|
|
|
// Fill structs with data
|
|
riffHeader.chunkID[0] = 'R';
|
|
riffHeader.chunkID[1] = 'I';
|
|
riffHeader.chunkID[2] = 'F';
|
|
riffHeader.chunkID[3] = 'F';
|
|
riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8;
|
|
riffHeader.format[0] = 'W';
|
|
riffHeader.format[1] = 'A';
|
|
riffHeader.format[2] = 'V';
|
|
riffHeader.format[3] = 'E';
|
|
|
|
waveFormat.subChunkID[0] = 'f';
|
|
waveFormat.subChunkID[1] = 'm';
|
|
waveFormat.subChunkID[2] = 't';
|
|
waveFormat.subChunkID[3] = ' ';
|
|
waveFormat.subChunkSize = 16;
|
|
waveFormat.audioFormat = 1;
|
|
waveFormat.numChannels = 1;
|
|
waveFormat.sampleRate = wave.sampleRate;
|
|
waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8;
|
|
waveFormat.blockAlign = wave.sampleSize/8;
|
|
waveFormat.bitsPerSample = wave.sampleSize;
|
|
|
|
waveData.subChunkID[0] = 'd';
|
|
waveData.subChunkID[1] = 'a';
|
|
waveData.subChunkID[2] = 't';
|
|
waveData.subChunkID[3] = 'a';
|
|
waveData.subChunkSize = wave.sampleCount*wave.sampleSize/8;
|
|
|
|
FILE *wavFile = fopen(fileName, "wb");
|
|
|
|
fwrite(&riffHeader, 1, sizeof(RiffHeader), wavFile);
|
|
fwrite(&waveFormat, 1, sizeof(WaveFormat), wavFile);
|
|
fwrite(&waveData, 1, sizeof(WaveFormat), wavFile);
|
|
|
|
fwrite(wave.data, 1, wave.sampleRate*wave.sampleSize/8, wavFile);
|
|
|
|
fclose(wavFile);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Buttons functions: sound generation
|
|
//--------------------------------------------------------------------------------------------
|
|
|
|
// Generate sound: Pickup/Coin
|
|
static void BtnPickupCoin(void)
|
|
{
|
|
ResetParams();
|
|
|
|
params.startFrequencyValue = 0.4f + frnd(0.5f);
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = frnd(0.1f);
|
|
params.decayTimeValue = 0.1f + frnd(0.4f);
|
|
params.sustainPunchValue = 0.3f + frnd(0.3f);
|
|
|
|
if (rnd(1))
|
|
{
|
|
params.changeSpeedValue = 0.5f + frnd(0.2f);
|
|
params.changeAmountValue = 0.2f + frnd(0.4f);
|
|
}
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Generate sound: Laser shoot
|
|
static void BtnLaserShoot(void)
|
|
{
|
|
ResetParams();
|
|
|
|
params.waveTypeValue = rnd(2);
|
|
|
|
if (params.waveTypeValue == 2 && rnd(1)) params.waveTypeValue = rnd(1);
|
|
|
|
params.startFrequencyValue = 0.5f + frnd(0.5f);
|
|
params.minFrequencyValue = params.startFrequencyValue - 0.2f - frnd(0.6f);
|
|
|
|
if (params.minFrequencyValue < 0.2f) params.minFrequencyValue = 0.2f;
|
|
|
|
params.slideValue = -0.15f - frnd(0.2f);
|
|
|
|
if (rnd(2) == 0)
|
|
{
|
|
params.startFrequencyValue = 0.3f + frnd(0.6f);
|
|
params.minFrequencyValue = frnd(0.1f);
|
|
params.slideValue = -0.35f - frnd(0.3f);
|
|
}
|
|
|
|
if (rnd(1))
|
|
{
|
|
params.squareDutyValue = frnd(0.5f);
|
|
params.dutySweepValue = frnd(0.2f);
|
|
}
|
|
else
|
|
{
|
|
params.squareDutyValue = 0.4f + frnd(0.5f);
|
|
params.dutySweepValue = -frnd(0.7f);
|
|
}
|
|
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = 0.1f + frnd(0.2f);
|
|
params.decayTimeValue = frnd(0.4f);
|
|
|
|
if (rnd(1)) params.sustainPunchValue = frnd(0.3f);
|
|
|
|
if (rnd(2) == 0)
|
|
{
|
|
params.phaserOffsetValue = frnd(0.2f);
|
|
params.phaserSweepValue = -frnd(0.2f);
|
|
}
|
|
|
|
if (rnd(1)) params.hpfCutoffValue = frnd(0.3f);
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Generate sound: Explosion
|
|
static void BtnExplosion(void)
|
|
{
|
|
ResetParams();
|
|
|
|
params.waveTypeValue = 3;
|
|
|
|
if (rnd(1))
|
|
{
|
|
params.startFrequencyValue = 0.1f + frnd(0.4f);
|
|
params.slideValue = -0.1f + frnd(0.4f);
|
|
}
|
|
else
|
|
{
|
|
params.startFrequencyValue = 0.2f + frnd(0.7f);
|
|
params.slideValue = -0.2f - frnd(0.2f);
|
|
}
|
|
|
|
params.startFrequencyValue *= params.startFrequencyValue;
|
|
|
|
if (rnd(4) == 0) params.slideValue = 0.0f;
|
|
if (rnd(2) == 0) params.repeatSpeedValue = 0.3f + frnd(0.5f);
|
|
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = 0.1f + frnd(0.3f);
|
|
params.decayTimeValue = frnd(0.5f);
|
|
|
|
if (rnd(1) == 0)
|
|
{
|
|
params.phaserOffsetValue = -0.3f + frnd(0.9f);
|
|
params.phaserSweepValue = -frnd(0.3f);
|
|
}
|
|
|
|
params.sustainPunchValue = 0.2f + frnd(0.6f);
|
|
|
|
if (rnd(1))
|
|
{
|
|
params.vibratoDepthValue = frnd(0.7f);
|
|
params.vibratoSpeedValue = frnd(0.6f);
|
|
}
|
|
|
|
if (rnd(2) == 0)
|
|
{
|
|
params.changeSpeedValue = 0.6f + frnd(0.3f);
|
|
params.changeAmountValue = 0.8f - frnd(1.6f);
|
|
}
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Generate sound: Powerup
|
|
static void BtnPowerup(void)
|
|
{
|
|
ResetParams();
|
|
|
|
if (rnd(1)) params.waveTypeValue = 1;
|
|
else params.squareDutyValue = frnd(0.6f);
|
|
|
|
if (rnd(1))
|
|
{
|
|
params.startFrequencyValue = 0.2f + frnd(0.3f);
|
|
params.slideValue = 0.1f + frnd(0.4f);
|
|
params.repeatSpeedValue = 0.4f + frnd(0.4f);
|
|
}
|
|
else
|
|
{
|
|
params.startFrequencyValue = 0.2f + frnd(0.3f);
|
|
params.slideValue = 0.05f + frnd(0.2f);
|
|
|
|
if (rnd(1))
|
|
{
|
|
params.vibratoDepthValue = frnd(0.7f);
|
|
params.vibratoSpeedValue = frnd(0.6f);
|
|
}
|
|
}
|
|
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = frnd(0.4f);
|
|
params.decayTimeValue = 0.1f + frnd(0.4f);
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Generate sound: Hit/Hurt
|
|
static void BtnHitHurt(void)
|
|
{
|
|
ResetParams();
|
|
|
|
params.waveTypeValue = rnd(2);
|
|
if (params.waveTypeValue == 2) params.waveTypeValue = 3;
|
|
if (params.waveTypeValue == 0) params.squareDutyValue = frnd(0.6f);
|
|
|
|
params.startFrequencyValue = 0.2f + frnd(0.6f);
|
|
params.slideValue = -0.3f - frnd(0.4f);
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = frnd(0.1f);
|
|
params.decayTimeValue = 0.1f + frnd(0.2f);
|
|
|
|
if (rnd(1)) params.hpfCutoffValue = frnd(0.3f);
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Generate sound: Jump
|
|
static void BtnJump(void)
|
|
{
|
|
ResetParams();
|
|
|
|
params.waveTypeValue = 0;
|
|
params.squareDutyValue = frnd(0.6f);
|
|
params.startFrequencyValue = 0.3f + frnd(0.3f);
|
|
params.slideValue = 0.1f + frnd(0.2f);
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = 0.1f + frnd(0.3f);
|
|
params.decayTimeValue = 0.1f + frnd(0.2f);
|
|
|
|
if (rnd(1)) params.hpfCutoffValue = frnd(0.3f);
|
|
if (rnd(1)) params.lpfCutoffValue = 1.0f - frnd(0.6f);
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Generate sound: Blip/Select
|
|
static void BtnBlipSelect(void)
|
|
{
|
|
ResetParams();
|
|
|
|
params.waveTypeValue = rnd(1);
|
|
if (params.waveTypeValue == 0) params.squareDutyValue = frnd(0.6f);
|
|
params.startFrequencyValue = 0.2f + frnd(0.4f);
|
|
params.attackTimeValue = 0.0f;
|
|
params.sustainTimeValue = 0.1f + frnd(0.1f);
|
|
params.decayTimeValue = frnd(0.2f);
|
|
params.hpfCutoffValue = 0.1f;
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Generate random sound
|
|
static void BtnRandomize(void)
|
|
{
|
|
params.startFrequencyValue = pow(frnd(2.0f) - 1.0f, 2.0f);
|
|
|
|
if (rnd(1)) params.startFrequencyValue = pow(frnd(2.0f) - 1.0f, 3.0f)+0.5f;
|
|
|
|
params.minFrequencyValue = 0.0f;
|
|
params.slideValue = pow(frnd(2.0f) - 1.0f, 5.0f);
|
|
|
|
if ((params.startFrequencyValue > 0.7f) && (params.slideValue > 0.2f)) params.slideValue = -params.slideValue;
|
|
if ((params.startFrequencyValue < 0.2f) && (params.slideValue < -0.05f)) params.slideValue = -params.slideValue;
|
|
|
|
params.deltaSlideValue = pow(frnd(2.0f) - 1.0f, 3.0f);
|
|
params.squareDutyValue = frnd(2.0f) - 1.0f;
|
|
params.dutySweepValue = pow(frnd(2.0f) - 1.0f, 3.0f);
|
|
params.vibratoDepthValue = pow(frnd(2.0f) - 1.0f, 3.0f);
|
|
params.vibratoSpeedValue = frnd(2.0f) - 1.0f;
|
|
//params.vibratoPhaseDelay = frnd(2.0f) - 1.0f;
|
|
params.attackTimeValue = pow(frnd(2.0f) - 1.0f, 3.0f);
|
|
params.sustainTimeValue = pow(frnd(2.0f) - 1.0f, 2.0f);
|
|
params.decayTimeValue = frnd(2.0f)-1.0f;
|
|
params.sustainPunchValue = pow(frnd(0.8f), 2.0f);
|
|
|
|
if (params.attackTimeValue + params.sustainTimeValue + params.decayTimeValue < 0.2f)
|
|
{
|
|
params.sustainTimeValue += 0.2f + frnd(0.3f);
|
|
params.decayTimeValue += 0.2f + frnd(0.3f);
|
|
}
|
|
|
|
params.lpfResonanceValue = frnd(2.0f) - 1.0f;
|
|
params.lpfCutoffValue = 1.0f - pow(frnd(1.0f), 3.0f);
|
|
params.lpfCutoffSweepValue = pow(frnd(2.0f) - 1.0f, 3.0f);
|
|
|
|
if (params.lpfCutoffValue < 0.1f && params.lpfCutoffSweepValue < -0.05f) params.lpfCutoffSweepValue = -params.lpfCutoffSweepValue;
|
|
|
|
params.hpfCutoffValue = pow(frnd(1.0f), 5.0f);
|
|
params.hpfCutoffSweepValue = pow(frnd(2.0f) - 1.0f, 5.0f);
|
|
params.phaserOffsetValue = pow(frnd(2.0f) - 1.0f, 3.0f);
|
|
params.phaserSweepValue = pow(frnd(2.0f) - 1.0f, 3.0f);
|
|
params.repeatSpeedValue = frnd(2.0f) - 1.0f;
|
|
params.changeSpeedValue = frnd(2.0f) - 1.0f;
|
|
params.changeAmountValue = frnd(2.0f) - 1.0f;
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
// Mutate current sound
|
|
static void BtnMutate(void)
|
|
{
|
|
if (rnd(1)) params.startFrequencyValue += frnd(0.1f) - 0.05f;
|
|
//if (rnd(1)) params.minFrequencyValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.slideValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.deltaSlideValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.squareDutyValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.dutySweepValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.vibratoDepthValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.vibratoSpeedValue += frnd(0.1f) - 0.05f;
|
|
//if (rnd(1)) params.vibratoPhaseDelay += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.attackTimeValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.sustainTimeValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.decayTimeValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.sustainPunchValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.lpfResonanceValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.lpfCutoffValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.lpfCutoffSweepValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.hpfCutoffValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.hpfCutoffSweepValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.phaserOffsetValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.phaserSweepValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.repeatSpeedValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.changeSpeedValue += frnd(0.1f) - 0.05f;
|
|
if (rnd(1)) params.changeAmountValue += frnd(0.1f) - 0.05f;
|
|
|
|
GeneratePlay();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Buttons functions: sound playing and export functions
|
|
//--------------------------------------------------------------------------------------------
|
|
|
|
// Play current sound
|
|
static void BtnPlaySound(void) { PlaySound(sound); }
|
|
|
|
// Load sound parameters file
|
|
static void BtnLoadSound(void)
|
|
{
|
|
// Open file dialog
|
|
const char *filters[] = { "*.rfx", "*.sfs" };
|
|
const char *fileName = tinyfd_openFileDialog("Load sound parameters file", currentPath, 2, filters, "Sound Param Files (*.rfx, *.sfs)", 0);
|
|
|
|
if (fileName != NULL)
|
|
{
|
|
LoadSoundParams(fileName);
|
|
//GeneratePlay();
|
|
}
|
|
}
|
|
|
|
// Save sound parameters file
|
|
static void BtnSaveSound(void)
|
|
{
|
|
char currrentPathFile[256];
|
|
|
|
// Add sample file name to currentPath
|
|
strcpy(currrentPathFile, currentPath);
|
|
strcat(currrentPathFile, "sound.rfx\0");
|
|
|
|
// Save file dialog
|
|
const char *filters[] = { "*.rfx", "*.sfs" };
|
|
const char *fileName = tinyfd_saveFileDialog("Save sound parameters file", currrentPathFile, 2, filters, "Sound Param Files (*.rfx, *.sfs)");
|
|
|
|
if (fileName != NULL) SaveSoundParams(fileName);
|
|
}
|
|
|
|
// Export current sound as .wav
|
|
static void BtnExportWav(void)
|
|
{
|
|
char currrentPathFile[256];
|
|
|
|
// Add sample file name to currentPath
|
|
strcpy(currrentPathFile, currentPath);
|
|
strcat(currrentPathFile, "sound.wav\0");
|
|
|
|
// Save file dialog
|
|
const char *filters[] = { "*.wav" };
|
|
const char *fileName = tinyfd_saveFileDialog("Save wave file", currrentPathFile, 1, filters, "Wave File (*.wav)");
|
|
|
|
Wave ewave = WaveCopy(wave);
|
|
WaveFormat(&ewave, wavFrequency, wavBitrate, 1);
|
|
|
|
SaveWAV(fileName, ewave);
|
|
|
|
UnloadWave(ewave);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Helper functions
|
|
//--------------------------------------------------------------------------------------------
|
|
|
|
// Get the extension for a filename
|
|
static const char *GetExtension(const char *fileName)
|
|
{
|
|
const char *dot = strrchr(fileName, '.');
|
|
if (!dot || dot == fileName) return "";
|
|
return (dot + 1);
|
|
}
|