3 Commits

Author SHA1 Message Date
3a0eb52fb1 feat(gui): raylib and it's window added 2023-06-17 15:28:19 +04:00
a93278f705 feat: adsr documentation 2023-06-17 00:19:16 +04:00
46ac7c9bba feat: phase accum documentation 2023-06-17 00:12:56 +04:00
4 changed files with 152 additions and 20 deletions

View File

@@ -1,3 +1,3 @@
#!/bin/bash
CC="${CXX:-cc}"
$CC -Wall -std=c11 ./main.c ./parser.c -lm -o ./bin/main
$CC -Wall -std=c11 ./main.c ./parser.c -lm -lraylib -o ./bin/main

60
docs/ADSR.md Normal file
View File

@@ -0,0 +1,60 @@
Certainly! Here's an example of generating an ADSR (Attack, Decay, Sustain, Release) envelope in Python:
```python
import numpy as np
import matplotlib.pyplot as plt
# Parameters
sample_rate = 44100 # Sample rate in Hz
duration = 5 # Duration of the envelope in seconds
# Time values
num_samples = int(duration * sample_rate)
time = np.arange(num_samples) / sample_rate
# ADSR parameters
attack_time = 0.5 # Attack time in seconds
decay_time = 0.3 # Decay time in seconds
sustain_level = 0.6 # Sustain level (0 to 1)
release_time = 1.0 # Release time in seconds
# Generate the ADSR envelope
envelope = np.zeros(num_samples)
attack_samples = int(attack_time * sample_rate)
decay_samples = int(decay_time * sample_rate)
release_samples = int(release_time * sample_rate)
# Attack phase
envelope[:attack_samples] = np.linspace(0, 1, num=attack_samples)
# Decay phase
decay_slope = (1 - sustain_level) / decay_samples
envelope[attack_samples:attack_samples + decay_samples] = np.linspace(1, sustain_level, num=decay_samples) - decay_slope * np.arange(decay_samples)
# Sustain phase
envelope[attack_samples + decay_samples:-release_samples] = sustain_level
# Release phase
release_slope = sustain_level / release_samples
envelope[-release_samples:] = sustain_level - release_slope * np.arange(release_samples)
# Normalize the envelope
envelope /= np.max(envelope)
# Plot the envelope
plt.plot(time, envelope)
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.title('ADSR Envelope')
plt.show()
```
In this example, we specify the sample rate and duration of the envelope. We define the ADSR parameters: attack time, decay time, sustain level, and release time.
We create an array to store the envelope values and initialize it with zeros. We calculate the number of samples for each phase based on the sample rate and duration.
We then calculate the envelope values for each phase. The attack phase increases linearly from 0 to 1. The decay phase decreases linearly from 1 to the sustain level. The sustain phase maintains a constant value equal to the sustain level. The release phase decreases linearly from the sustain level to 0.
After generating the envelope, we normalize it to ensure the maximum value is 1. Finally, we plot the envelope using Matplotlib.
You can modify the ADSR parameters to create different envelope shapes or experiment with adding modulation to create more dynamic and expressive sounds.

39
docs/PhaseAccumulation.md Normal file
View File

@@ -0,0 +1,39 @@
Certainly! Here's a simple example of phase accumulation in Python code:
```python
import numpy as np
import matplotlib.pyplot as plt
# Parameters
sample_rate = 44100 # Sample rate in Hz
frequency = 440 # Frequency of the oscillator in Hz
duration = 1 # Duration of the generated waveform in seconds
# Calculate the phase increment per sample
phase_increment = 2 * np.pi * frequency / sample_rate
# Initialize phase and time arrays
num_samples = int(duration * sample_rate)
phase = np.zeros(num_samples)
time = np.arange(num_samples) / sample_rate
# Perform phase accumulation
for i in range(1, num_samples):
phase[i] = phase[i - 1] + phase_increment
# Generate the waveform (sine wave) based on the accumulated phase
waveform = np.sin(phase)
# Plot the waveform
plt.plot(time, waveform)
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.title('Phase Accumulation Example - Sine Wave')
plt.show()
```
In this example, we specify the sample rate, frequency, and duration of the waveform. We calculate the phase increment per sample based on the desired frequency. Then, we initialize arrays to store the phase values and time values. By iterating through each sample, we perform phase accumulation by adding the phase increment to the previous phase value.
Finally, we generate a sine wave by taking the sine of the accumulated phase values. The resulting waveform is plotted using Matplotlib.
This code demonstrates the basic concept of phase accumulation, where the phase of an oscillator is incremented over time to generate a periodic waveform. You can modify the parameters, try different waveforms, or experiment with phase modulation to create more complex sounds.

71
main.c
View File

@@ -4,6 +4,8 @@
#include "math.h"
#include "parser.h"
#include "raylib.h"
#define SAMPLE_RATE 48000.f
#define BPM 120.f
#define BEAT_DURATION 60.f/BPM
@@ -11,19 +13,19 @@
#define VOLUME 0.5f
#define ATTACK_MS 100.f
#define PI 3.1415926535f
#define SYNTH_PI 3.1415926535f
//------------------------------------------------------------------------------------
// General Sound
// General SynthSound
//------------------------------------------------------------------------------------
typedef struct Sound {
typedef struct SynthSound {
float* samples;
size_t sample_count;
} Sound;
} SynthSound;
// frees the original sounds
Sound concat_sounds(Sound* sounds, size_t count) {
SynthSound concat_sounds(SynthSound* sounds, size_t count) {
size_t total_count = 0;
for (size_t i = 0; i < count; i++) {
total_count += sounds[i].sample_count;
@@ -41,7 +43,7 @@ Sound concat_sounds(Sound* sounds, size_t count) {
free(sounds[i].samples);
}
Sound result = {
SynthSound result = {
.samples = total,
.sample_count = total_count
};
@@ -75,7 +77,7 @@ typedef struct OscillatorGenerationParameter {
float sample;
} OscillatorGenerationParameter;
static Sound get_init_samples(float duration) {
static SynthSound get_init_samples(float duration) {
size_t sample_count = (size_t)(duration * SAMPLE_RATE);
float* samples = malloc(sizeof(float) * sample_count);
@@ -83,7 +85,7 @@ static Sound get_init_samples(float duration) {
samples[(int)i] = i;
}
Sound res = {
SynthSound res = {
.samples = samples,
.sample_count = sample_count
};
@@ -96,7 +98,7 @@ static float pos(float hz, float x) {
}
float sineosc(float hz, float x) {
return sinf(x * (2.f * PI * hz / SAMPLE_RATE));
return sinf(x * (2.f * SYNTH_PI * hz / SAMPLE_RATE));
}
static float sign(float v) {
@@ -138,9 +140,9 @@ float multiosc(OscillatorGenerationParameter param) {
return osc_sample;
}
static Sound freq(float duration, OscillatorParameterList osc) {
Sound samples = get_init_samples(duration);
// Sound attack = get_attack_samples();
static SynthSound freq(float duration, OscillatorParameterList osc) {
SynthSound samples = get_init_samples(duration);
// SynthSound attack = get_attack_samples();
float* output = malloc(sizeof(float) * samples.sample_count);
for (int i = 0; i < samples.sample_count; i++) {
@@ -179,7 +181,7 @@ static Sound freq(float duration, OscillatorParameterList osc) {
*/
// return zipped array
Sound res = {
SynthSound res = {
.samples = output,
.sample_count = samples.sample_count
};
@@ -188,7 +190,7 @@ static Sound freq(float duration, OscillatorParameterList osc) {
}
/*
static Sound get_attack_samples() {
static SynthSound get_attack_samples() {
float attack_time = 0.001 * ATTACK_MS;
size_t sample_count = (size_t)(attack_time * SAMPLE_RATE);
float* attack = malloc(sizeof(float) * sample_count);
@@ -201,7 +203,7 @@ static Sound get_attack_samples() {
attack[j] = fmin(i, 1.0);
}
Sound res = {
SynthSound res = {
.samples = attack,
.sample_count = sample_count
};
@@ -266,7 +268,7 @@ size_t get_semitone_shift(char* target_note) {
return get_semitone_shift_internal("A4", target_note);
}
Sound note(size_t semitone, float beats) {
SynthSound note(size_t semitone, float beats) {
float hz = get_hz_by_semitone(semitone);
float duration = beats * BEAT_DURATION;
@@ -294,7 +296,7 @@ Sound note(size_t semitone, float beats) {
return freq(duration, parameters);
}
Sound get_note_sound(Note input) {
SynthSound get_note_sound(Note input) {
float length = 1.f / input.length;
size_t semitone_shift = get_semitone_shift(input.name);
return note(semitone_shift, length);
@@ -379,18 +381,44 @@ void pack(uint16_t* d, size_t length) {
//------------------------------------------------------------------------------------
int main(int argc, char **argv) {
const size_t width = 1280;
const size_t height = 720;
InitWindow(width, height, "SeeSynth - v0.1");
//SetTargetFPS(60);
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
char* input = "A4-4 A4-4 A4-4 A4-4 A4-2 A4-4 A4-4 A4-4 A4-4 A4-4 A4-2 D5-4 D5-4 D5-4 D5-4 D5-4 D5-4 D5-2 C5-4 C5-4 C5-4 C5-4 C5-4 C5-4 C5-2 G4-2 ";
char* buf = malloc(strlen(input) + 1);
strcpy(buf, input);
NoteArray note_array = parse_notes(buf, strlen(buf));
Sound* sounds = malloc(sizeof(Sound) * note_array.count);
SynthSound* sounds = malloc(sizeof(SynthSound) * note_array.count);
for (size_t i = 0; i < note_array.count; i++) {
Note note = note_array.notes[i];
sounds[i] = get_note_sound(note);
}
Sound song = concat_sounds(sounds, note_array.count);
SynthSound song = concat_sounds(sounds, note_array.count);
uint16_t* song_pcm = malloc(sizeof(uint16_t) * song.sample_count);
for (size_t i = 0; i < song.sample_count; i++) {
song_pcm[i] = toInt16Sample(song.samples[i]);
@@ -398,5 +426,10 @@ int main(int argc, char **argv) {
pack(song_pcm, song.sample_count);
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}