diff options
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..553ee89 --- /dev/null +++ b/src/main.c @@ -0,0 +1,177 @@ +#include <stdio.h> +#include <alsa/asoundlib.h> +#include <math.h> +#include <raylib.h> +#include <pthread.h> +#include <stdbool.h> + +#define SAMPLE_RATE 48000 +#define PERIOD_SIZE 480 +#define AMPLITUDE 10000 + +#define check(ret) \ + do { \ + int res = (ret); \ + if (res < 0) { \ + fprintf(stderr, "%s:%d ERROR: %s (%d)\n", \ + __FILE__, __LINE__, snd_strerror(res), res); \ + exit(1); \ + } \ + } while (0) + +typedef struct { + snd_pcm_t * pcm; + float *freqs; + size_t freqs_count; + bool should_stop; +} sound_thread_meta; + +int set_hw_params(snd_pcm_t *pcm) { + snd_pcm_hw_params_t *hw_params; + + snd_pcm_hw_params_alloca(&hw_params); + + check(snd_pcm_hw_params_any(pcm, hw_params)); + + unsigned int resample = 1; + check(snd_pcm_hw_params_set_rate_resample(pcm, hw_params, resample)); + check(snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)); + check(snd_pcm_hw_params_set_format(pcm, hw_params, SND_PCM_FORMAT_S16_LE)); + check(snd_pcm_hw_params_set_channels(pcm, hw_params, 1)); + check(snd_pcm_hw_params_set_rate(pcm, hw_params, SAMPLE_RATE, 0)); + snd_pcm_uframes_t period_size = PERIOD_SIZE; + check(snd_pcm_hw_params_set_period_size_near(pcm, hw_params, &period_size, 0)); + snd_pcm_uframes_t buffer_size = period_size * 3; + check(snd_pcm_hw_params_set_buffer_size_near(pcm, hw_params, &buffer_size)); + check(snd_pcm_hw_params(pcm, hw_params)); + + return 0; +} + +short *gen_sine(short *buffer, size_t sample_count, float freq, float *phase) { + for (size_t i = 0; i < sample_count; i++) { + buffer[i] = AMPLITUDE * sinf(*phase); + *phase += 2 * M_PI * freq / SAMPLE_RATE; + if (*phase >= 2 * M_PI) *phase -= 2 * M_PI; + } + + return buffer; +} + +short *gen_square(short *buffer, size_t sample_count, float freq, float *phase) { + int samples_full_cycle = (float)SAMPLE_RATE / freq; + int samples_half_cycle = samples_full_cycle / 2.0f; + for (size_t i = 0; i < sample_count; i++) { + buffer[i] = *phase < samples_half_cycle ? AMPLITUDE : -AMPLITUDE; + *phase = (((int)*phase + 1) % samples_full_cycle); + } + return buffer; +} + +short *gen_saw(short *buffer, size_t sample_count, float freq, float *phase) { + int samples_full_cycle = (float)SAMPLE_RATE / freq; + int step = AMPLITUDE / samples_full_cycle; + + for (size_t i = 0; i < sample_count; i++) { + buffer[i] = *phase; + + *phase += step; + if (*phase >= AMPLITUDE) { + *phase = -AMPLITUDE; + } + } + + return buffer; +} + +void *sound_thread_generate(void *ptr) { + sound_thread_meta *meta = ptr; + + short buffer[PERIOD_SIZE]; + float phase = 0.0f; + + float prev_freq = meta->freqs[0]; + + while (!meta->should_stop) { + if (meta->freqs_count <= 0) { + continue; + } + + float freq = meta->freqs[0]; + if (freq != prev_freq) { + printf("%f\n", freq); + prev_freq = freq; + } + + gen_saw(buffer, PERIOD_SIZE, freq, &phase); + + snd_pcm_sframes_t written = snd_pcm_writei(meta->pcm, buffer, PERIOD_SIZE); + if (written < 0) { + snd_pcm_prepare(meta->pcm); // recover from xrun + } + } + + return NULL; +} + +typedef struct { + char key; + float freq; +} freq_map; + +int main() { + snd_pcm_t *sound_device; + int err; + + err = snd_pcm_open(&sound_device, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) { + printf("error: failed to open device: %s", snd_strerror(err)); + return 1; + } + + err = set_hw_params(sound_device); + if (err < 0) { + printf("error: failed to set parameters: %s", snd_strerror(err)); + return 1; + } + + SetConfigFlags(FLAG_WINDOW_RESIZABLE); + InitWindow(800, 800, "crynth"); + SetTargetFPS(60); + + float freqs[10]; + + freqs[0] = 220; + + sound_thread_meta soundgen = { + .pcm = sound_device, + .freqs = freqs, + .freqs_count = 1, + .should_stop = false, + }; + + pthread_t sound_thread; + pthread_create(&sound_thread, NULL, sound_thread_generate, &soundgen); + + while (!WindowShouldClose()) { + if (IsKeyDown(KEY_SPACE)) { + soundgen.freqs[0] = 900; + } else { + soundgen.freqs[0] = 500; + } + + BeginDrawing(); + ClearBackground(RAYWHITE); + EndDrawing(); + } + + soundgen.should_stop = true; + CloseWindow(); + + pthread_join(sound_thread, NULL); + + check(snd_pcm_drop(sound_device)); + check(snd_pcm_close(sound_device)); + + return 0; +} |
