Compare commits
10 Commits
feature/os
...
feature/gu
| Author | SHA1 | Date | |
|---|---|---|---|
|
2c3df4baed
|
|||
|
aeeab6977f
|
|||
|
1ac275906f
|
|||
|
0eb203f9f4
|
|||
|
d7f4538418
|
|||
|
0cdac55d27
|
|||
|
10bf0c6a06
|
|||
| 320a3cc8e0 | |||
|
a93278f705
|
|||
|
46ac7c9bba
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/bin
|
||||
.DS_Store
|
||||
/Debug/
|
||||
*.wav
|
||||
*.wav
|
||||
*.dSYM
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -4,5 +4,6 @@
|
||||
],
|
||||
"files.associations": {
|
||||
"algorithm": "c"
|
||||
}
|
||||
},
|
||||
"FSharp.suggestGitignore": false,
|
||||
}
|
||||
32
.vscode/tasks.json
vendored
Normal file
32
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "cppbuild",
|
||||
"label": "C/C++: clang сборка активного файла",
|
||||
"command": "/usr/bin/clang",
|
||||
"args": [
|
||||
"-fcolor-diagnostics",
|
||||
"-fansi-escape-codes",
|
||||
"-g",
|
||||
"${file}",
|
||||
"${fileDirname}/parser.c",
|
||||
"-lm",
|
||||
"-lraylib",
|
||||
"-o",
|
||||
"${fileDirname}/bin/${fileBasenameNoExtension}"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${fileDirname}"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"detail": "Задача создана отладчиком."
|
||||
}
|
||||
],
|
||||
"version": "2.0.0"
|
||||
}
|
||||
2
build.sh
2
build.sh
@@ -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
60
docs/ADSR.md
Normal 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
39
docs/PhaseAccumulation.md
Normal 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.
|
||||
364
main.c
364
main.c
@@ -4,26 +4,148 @@
|
||||
#include "math.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#define SAMPLE_RATE 48000.f
|
||||
#define BPM 120.f
|
||||
#define BEAT_DURATION 60.f/BPM
|
||||
#define PITCH_STANDARD 440.f
|
||||
#define VOLUME 0.5f
|
||||
#define ATTACK_MS 100.f
|
||||
#define STREAM_BUFFER_SIZE 4096
|
||||
|
||||
#define PI 3.1415926535f
|
||||
#define SYNTH_PI 3.1415926535f
|
||||
#define SYNTH_VOLUME 0.5f
|
||||
|
||||
#define WINDOW_WIDTH 640
|
||||
#define WINDOW_HEIGHT 480
|
||||
|
||||
#define write_log(format,args...) do { \
|
||||
printf(format, ## args); \
|
||||
} while(0)
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// General Sound
|
||||
// Ring Buffer
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
typedef struct Sound {
|
||||
typedef struct RingBuffer {
|
||||
float* items;
|
||||
size_t head;
|
||||
size_t tail;
|
||||
int is_full;
|
||||
int is_empty;
|
||||
size_t size;
|
||||
} RingBuffer;
|
||||
|
||||
RingBuffer ring_buffer_init(size_t buffer_size) {
|
||||
RingBuffer buffer = {
|
||||
.items = calloc(buffer_size, sizeof(float)),
|
||||
.head = 0,
|
||||
.tail = 0,
|
||||
.is_full = 0,
|
||||
.is_empty = 1,
|
||||
.size = buffer_size
|
||||
};
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ring_buffer_reset(RingBuffer* me) {
|
||||
me->head = 0;
|
||||
me->tail = 0;
|
||||
me->is_full = 0;
|
||||
}
|
||||
|
||||
// +
|
||||
static void advance_pointer(RingBuffer* me) {
|
||||
if(me->is_full) {
|
||||
me->tail++;
|
||||
if (me->tail == me->size) {
|
||||
me->tail = 0;
|
||||
}
|
||||
}
|
||||
me->head++;
|
||||
if (me->head == me->size) {
|
||||
me->head = 0;
|
||||
}
|
||||
size_t is_full = me->head == me->tail ? 1 : 0;
|
||||
me->is_full = is_full;
|
||||
}
|
||||
|
||||
// -
|
||||
static void retreat_pointer(RingBuffer* me) {
|
||||
me->is_full = 0;
|
||||
me->tail++;
|
||||
if (me->tail == me->size) {
|
||||
me->tail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ring_buffer_write(RingBuffer* buffer, float* data, size_t count) {
|
||||
if (buffer->is_full || buffer->head + count > buffer->size) {
|
||||
write_log("[WARN] Trying to overfill the ring buffer: \n\tIsFull:%d\n\tHead:%zu\n\tCount:%zu\n\t",
|
||||
buffer->is_full,
|
||||
buffer->head,
|
||||
count);
|
||||
return;
|
||||
}
|
||||
buffer->is_empty = 0;
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
buffer->items[buffer->head] = data[i];
|
||||
advance_pointer(buffer);
|
||||
}
|
||||
//me->is_empty = is_full && (me->head == me->tail);
|
||||
}
|
||||
|
||||
int ring_buffer_read(RingBuffer* buffer, float* output, size_t count) {
|
||||
if (buffer->is_empty) {
|
||||
write_log("[WARN] Trying to read empty buffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
output[i] = buffer->items[buffer->tail];
|
||||
retreat_pointer(buffer);
|
||||
}
|
||||
buffer->is_empty = !buffer->is_full && (buffer->head == buffer->tail);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t ring_buffer_size(RingBuffer* buffer) {
|
||||
size_t size = buffer->size;
|
||||
if(!buffer->is_full) {
|
||||
if(buffer->head >= buffer->tail) {
|
||||
size = (buffer->head - buffer->tail);
|
||||
}
|
||||
else {
|
||||
size = (buffer->size + buffer->head - buffer->tail);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void ring_buffer_print(RingBuffer* me) {
|
||||
write_log("[INFO] The ring buffer: \n\tIsFull:%d\n\tIsEmpty:%d\n\tHead:%zu\n\tTail:%zu\n\t",
|
||||
me->is_full,
|
||||
me->is_empty,
|
||||
me->head,
|
||||
me->tail);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// General SynthSound
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
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 +163,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 +197,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 +205,7 @@ static Sound get_init_samples(float duration) {
|
||||
samples[(int)i] = i;
|
||||
}
|
||||
|
||||
Sound res = {
|
||||
SynthSound res = {
|
||||
.samples = samples,
|
||||
.sample_count = sample_count
|
||||
};
|
||||
@@ -96,7 +218,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 +260,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++) {
|
||||
@@ -178,8 +300,22 @@ static Sound freq(float duration, OscillatorParameterList osc) {
|
||||
}
|
||||
*/
|
||||
|
||||
// if (samples.sample_count > 1024) {
|
||||
// samples.sample_count = 1024;
|
||||
// }
|
||||
// //todo: move to somewhere
|
||||
// for (size_t i = 0; i < 1024; i++) {
|
||||
// synth_sound.samples[i] = 0.0f;
|
||||
// }
|
||||
|
||||
// for (size_t i = 0; i < samples.sample_count; i++) {
|
||||
// synth_sound.samples[i] = output[i];
|
||||
// }
|
||||
// synth_sound.sample_count = samples.sample_count;
|
||||
|
||||
|
||||
// return zipped array
|
||||
Sound res = {
|
||||
SynthSound res = {
|
||||
.samples = output,
|
||||
.sample_count = samples.sample_count
|
||||
};
|
||||
@@ -188,7 +324,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 +337,7 @@ static Sound get_attack_samples() {
|
||||
attack[j] = fmin(i, 1.0);
|
||||
}
|
||||
|
||||
Sound res = {
|
||||
SynthSound res = {
|
||||
.samples = attack,
|
||||
.sample_count = sample_count
|
||||
};
|
||||
@@ -214,7 +350,7 @@ static Sound get_attack_samples() {
|
||||
// Synth
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
static size_t get_semitone_shift_internal(char* root_note, char* target_note) {
|
||||
static int get_semitone_shift_internal(char* root_note, char* target_note) {
|
||||
char* pitch_classes[12] =
|
||||
{ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
|
||||
|
||||
@@ -258,21 +394,21 @@ static size_t get_semitone_shift_internal(char* root_note, char* target_note) {
|
||||
(target_pitch_class - root_pitch_class);
|
||||
}
|
||||
|
||||
static float get_hz_by_semitone(size_t semitone) {
|
||||
static float get_hz_by_semitone(int semitone) {
|
||||
return PITCH_STANDARD * powf(powf(2.f, (1.f / 12.f)), semitone);
|
||||
}
|
||||
|
||||
size_t get_semitone_shift(char* target_note) {
|
||||
int get_semitone_shift(char* target_note) {
|
||||
return get_semitone_shift_internal("A4", target_note);
|
||||
}
|
||||
|
||||
Sound note(size_t semitone, float beats) {
|
||||
SynthSound note(int semitone, float beats) {
|
||||
float hz = get_hz_by_semitone(semitone);
|
||||
float duration = beats * BEAT_DURATION;
|
||||
|
||||
OscillatorParameter first = {
|
||||
.osc = Saw,
|
||||
.freq = hz/4.f
|
||||
.osc = Square,
|
||||
.freq = hz
|
||||
};
|
||||
|
||||
OscillatorParameter second = {
|
||||
@@ -285,18 +421,18 @@ Sound note(size_t semitone, float beats) {
|
||||
.freq = hz - 1.f
|
||||
};
|
||||
|
||||
OscillatorParameter oscArray[] = { first, second, third };
|
||||
OscillatorParameter oscArray[] = { first/*, second, third */};
|
||||
OscillatorParameterList parameters = {
|
||||
.array = oscArray,
|
||||
.count = 3
|
||||
.count = 1
|
||||
};
|
||||
|
||||
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);
|
||||
int semitone_shift = get_semitone_shift(input.name);
|
||||
return note(semitone_shift, length);
|
||||
}
|
||||
|
||||
@@ -374,23 +510,189 @@ void pack(uint16_t* d, size_t length) {
|
||||
write_file("output.wav", buffer, fileSize);
|
||||
}
|
||||
|
||||
size_t detect_note_pressed(Note* note) {
|
||||
size_t is_pressed = 0;
|
||||
note->length = 8;
|
||||
if (IsKeyPressed(KEY_A)) {
|
||||
strcpy(note->name, "A4");
|
||||
is_pressed = 1;
|
||||
}
|
||||
if (IsKeyPressed(KEY_B)) {
|
||||
strcpy(note->name, "B4");
|
||||
is_pressed = 1;
|
||||
}
|
||||
if (IsKeyPressed(KEY_C)) {
|
||||
strcpy(note->name, "C4");
|
||||
is_pressed = 1;
|
||||
}
|
||||
if (IsKeyPressed(KEY_D)) {
|
||||
strcpy(note->name, "D4");
|
||||
is_pressed = 1;
|
||||
}
|
||||
if (IsKeyPressed(KEY_E)) {
|
||||
strcpy(note->name, "E4");
|
||||
is_pressed = 1;
|
||||
}
|
||||
if (IsKeyPressed(KEY_F)) {
|
||||
strcpy(note->name, "F4");
|
||||
is_pressed = 1;
|
||||
}
|
||||
if (IsKeyPressed(KEY_G)) {
|
||||
strcpy(note->name, "G4");
|
||||
is_pressed = 1;
|
||||
}
|
||||
return is_pressed;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// UI
|
||||
//------------------------------------------------------------------------------------
|
||||
/*
|
||||
int get_zero_crossing(SynthSound* sound) {
|
||||
int zero_crossing_index = 0;
|
||||
for (size_t i = 1; i < sound->sample_count; i++)
|
||||
{
|
||||
if (sound->samples[i] >= 0.0f && sound->samples[i-1] < 0.0f) // zero-crossing
|
||||
{
|
||||
zero_crossing_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return zero_crossing_index;
|
||||
}
|
||||
|
||||
Vector2* GetVisualSignal(SynthSound* sound, int zero_crossing_index)
|
||||
{
|
||||
const int signal_amp = 300;
|
||||
|
||||
Vector2* signal_points = malloc(sizeof(Vector2) * STREAM_BUFFER_SIZE);
|
||||
|
||||
const float screen_vertical_midpoint = (WINDOW_HEIGHT/2);
|
||||
for (size_t point_idx = 0; point_idx < sound->sample_count; point_idx++)
|
||||
{
|
||||
const int signal_idx = (point_idx + zero_crossing_index) % STREAM_BUFFER_SIZE;
|
||||
signal_points[point_idx].x = (float)point_idx + WINDOW_WIDTH;
|
||||
signal_points[point_idx].y = screen_vertical_midpoint + (int)(sound->samples[signal_idx] * 300);
|
||||
}
|
||||
|
||||
return signal_points;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Main
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "SeeSynth - v0.1");
|
||||
SetTargetFPS(60);
|
||||
|
||||
Note current_note = {
|
||||
.length = 1,
|
||||
.name = malloc(sizeof(char) * 3)
|
||||
};
|
||||
|
||||
SynthSound sound = {
|
||||
.sample_count = 0
|
||||
};
|
||||
int sound_played_count = 0;
|
||||
float temp_buffer[STREAM_BUFFER_SIZE];
|
||||
RingBuffer ring_buffer = ring_buffer_init(STREAM_BUFFER_SIZE);
|
||||
|
||||
|
||||
InitAudioDevice();
|
||||
SetMasterVolume(SYNTH_VOLUME);
|
||||
SetAudioStreamBufferSizeDefault(STREAM_BUFFER_SIZE);
|
||||
AudioStream synth_stream = LoadAudioStream(SAMPLE_RATE, sizeof(float) * 8, 1);//float*8
|
||||
SetAudioStreamVolume(synth_stream, 0.5f);
|
||||
|
||||
PlayAudioStream(synth_stream);
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update Audio states
|
||||
//----------------------------------------------------------------------------------
|
||||
// Fill ring buffer from current sound
|
||||
size_t size_for_buffer = 0;
|
||||
if (!ring_buffer.is_full && sound.sample_count != sound_played_count) {
|
||||
write_log("[INFO] IsFull:%d Samples:%zu Played:%zu\n",
|
||||
ring_buffer.is_full,
|
||||
sound.sample_count,
|
||||
sound_played_count);
|
||||
|
||||
// how many samples need write
|
||||
size_t size_to_fill = 0;
|
||||
|
||||
if ((sound.sample_count - sound_played_count) > ring_buffer.size) {
|
||||
size_to_fill = ring_buffer.size;
|
||||
} else {
|
||||
size_to_fill = sound.sample_count - sound_played_count;
|
||||
}
|
||||
|
||||
write_log("[INFO] SizeToFill:%zu\n", size_to_fill);
|
||||
for (size_t i = 0; i < size_to_fill; i++) {
|
||||
temp_buffer[i] = sound.samples[i];
|
||||
}
|
||||
|
||||
ring_buffer_write(&ring_buffer, temp_buffer, size_to_fill);
|
||||
sound_played_count += size_to_fill;
|
||||
}
|
||||
|
||||
// Play ring-buffered audio
|
||||
if (IsAudioStreamProcessed(synth_stream) && !ring_buffer.is_empty) {
|
||||
size_t size_to_read = ring_buffer_size(&ring_buffer);
|
||||
|
||||
write_log("Samples to play:%zu \n", size_to_read);
|
||||
//todo: try to start reading directly from ring buffer, avoiding temp_buffer
|
||||
ring_buffer_read(&ring_buffer, temp_buffer, size_to_read);
|
||||
// can try the SetAudioStreamCallback
|
||||
UpdateAudioStream(synth_stream, temp_buffer, size_to_read);
|
||||
// can overwrite the ring buffer to avoid that
|
||||
if (sound.sample_count == sound_played_count) {
|
||||
ring_buffer_reset(&ring_buffer);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Update On Input
|
||||
//----------------------------------------------------------------------------------
|
||||
if (detect_note_pressed(¤t_note)) {
|
||||
sound = get_note_sound(current_note);
|
||||
sound_played_count = 0;
|
||||
write_log("Note played: %s\n", current_note.name);
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(RAYWHITE);
|
||||
// int zero_crossing = get_zero_crossing(&sound);
|
||||
// Vector2* visual_signal = GetVisualSignal(&sound, zero_crossing);
|
||||
// DrawLineStrip(visual_signal, STREAM_BUFFER_SIZE - zero_crossing, RED);
|
||||
|
||||
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
|
||||
DrawFPS(0,0);
|
||||
|
||||
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 +700,13 @@ int main(int argc, char **argv) {
|
||||
|
||||
pack(song_pcm, song.sample_count);
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
StopAudioStream(synth_stream);
|
||||
UnloadAudioStream(synth_stream);
|
||||
CloseAudioDevice();
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user