From d4c386f61c360192af0c0298d018bd2cce85d036 Mon Sep 17 00:00:00 2001 From: grm Date: Sat, 16 Aug 2025 12:12:33 +0300 Subject: Some initial docu and small ui updates --- src/synth_engine_v2.c | 83 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 15 deletions(-) (limited to 'src/synth_engine_v2.c') diff --git a/src/synth_engine_v2.c b/src/synth_engine_v2.c index 8740fe0..14d3e7e 100644 --- a/src/synth_engine_v2.c +++ b/src/synth_engine_v2.c @@ -123,7 +123,6 @@ float prev_sample = 0.0f; biquad_filter_t biquad = {0}; - void do_fliter(synth_t *synth, float *sample, unsigned int sample_rate, int frame) { @@ -209,8 +208,6 @@ get_max_sample(synth_t *synth, int test_size) return max; } -# include // uint32_t - float Q_rsqrt(float number) { union { @@ -222,6 +219,19 @@ float Q_rsqrt(float number) return conv.f; } +/** + * @brief generate a sample from the currently selected generator + * + * Applies ADSR, filter and merges all the `voices` using root mean square + * + * @param synth synth struct + * @param sample_rate sample rate + * @param frame # of current frame + * + * @return float returns the generated sample + * + * @note This is called SAMPLE_RATE times per second (on average), needs to be quick + */ float make_sample(synth_t * synth, unsigned int sample_rate, int frame) { @@ -263,11 +273,9 @@ make_sample(synth_t * synth, unsigned int sample_rate, int frame) sample_rate); } - /* filter */ do_fliter(synth, &sample, sample_rate, frame); - sample = CC_GET(gain) * sample; // band stop for high freqs @@ -313,6 +321,25 @@ increment_synth(synth_t *synth) float prev = 0; +/** + * @brief generates a single frame, also handles the delay + * + * each frame in the current implementation is a pair of samples one for the + * left channel and one for the right. We first check with notes_active if + * there are any notes still ringing so we can set the active timer to 0. Then + * we get the sample from the generators, and we blend any possible delay sound + * from before as well as add the new sample to the delay for future use. Then + * we increment the synth counters to be ready for the next sample and we make + * sure to write our sample to the viz buffer as well for visuals. + * + * @param outputBuffer where to store the generated samples + * @param synth synth struct + * @param frame # of frame in the buffer + * + * @return void void + * + * @note <--> + */ void get_frame(void *outputBuffer, synth_t *synth, int frame) { @@ -346,8 +373,6 @@ get_frame(void *outputBuffer, synth_t *synth, int frame) add_to_delay(synth, sample); - - //sample = clamp(sample, -1, 1); *out++ = sample; @@ -397,7 +422,37 @@ void smooth_buffer(float *buffer, int frames_per_buffer, float smooth_factor) { } } - +/** + * @brief portaudio callback to generate next batch of samples + * + * For a given SAMPLE_RATE we need to create SAMPLE_RATE frames per second so + * we get sound from the speaker. A frame is all the different channel values + * for a given timestamp. A stereo setup (like this) has 2 samples in each + * frame (left and right). The soundcard requests frames in a buffer where we + * control the size. The more frames the buffer has, the more latency we + * introduce to the system since in order for our changes in the synth to take + * effect the soundcard needs to request the next buffer. We handle this by + * picking a low enough framesPerBuffer number (FRAMES_PER_BUFFER). Since each + * second the soundcard needs to generate SAMPLE_RATE frames, it will call the + * sound_gen function SAMPLE_RATE / FRAMES_PER_BUFFER times. It follows that + * this function should be as fast as possible for realtime performance (with + * the upper bound of 1 / (SAMPLE_RATE / FRAMES_PER_BUFFER) seconds). Failing + * to meet this *will* result in loud clicks and noise. + * + * This function is called asynchronously by the portaudio thread. It prepares + * all the CCs and the fills the outputBuffer frame by frame. + * + * @param inputBuffer unused + * @param outputBuffer where the resulting samples are written + * @param framesPerBuffer how many frames the buffer has + * @param timeInfo unused + * @param statusFlags unused + * @param synthData user data, contains the synth struct + * + * @return int paContinue(0) on success + * + * @note this funcion shouldn't fail + */ int sound_gen(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, @@ -406,12 +461,10 @@ sound_gen(const void *inputBuffer, void *outputBuffer, void *synthData) { synth_t *synth = (synth_t*)synthData; - float *out = (float*)outputBuffer; - if (!synth->sound_active) return 0; //paContinue; float buffer[2 * FRAMES_PER_BUFFER]; - + float *out = (float*)outputBuffer; (void) timeInfo; (void) statusFlags; (void) inputBuffer; @@ -443,7 +496,7 @@ sound_gen(const void *inputBuffer, void *outputBuffer, clock_t end = clock(); double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; if (time_spent > (double)FRAMES_PER_BUFFER / SAMPLE_RATE) { - printf("To generate %d samples per second we need to generate a sample every 1 / %d second. Since we generate samples by batching them in groups of %d, each group should take longer than %f. This one took %f\n", SAMPLE_RATE, SAMPLE_RATE, FRAMES_PER_BUFFER, (float)FRAMES_PER_BUFFER / SAMPLE_RATE, time_spent); + printf("To generate %d samples per second we need to generate a sample every 1 / %d second. Since we generate samples by batching them in groups of %d, each group should not take longer than %f. This one took %f\n", SAMPLE_RATE, SAMPLE_RATE, FRAMES_PER_BUFFER, (float)FRAMES_PER_BUFFER / SAMPLE_RATE, time_spent); } return paContinue; @@ -558,11 +611,11 @@ init_synth(void) synth->viz.fft_output_buffer = (float *)calloc(sizeof(float), RING_SIZE * 8); synth->viz.fft_smooth_buffer = (float *)calloc(sizeof(float), RING_SIZE * 8); - synth->viz.spectrum_enabled = 0; + synth->viz.spectrum_enabled = 1; synth->viz.wave_enabled = 0; synth->viz.adsr_enabled = 0; - synth->viz.adsr_graph_enabled = 1; - synth->viz.osc_enabled = 0; + synth->viz.adsr_graph_enabled = 0; + synth->viz.osc_enabled = 1; synth->viz.freeze = 0; synth->viz.tmp_index = 0; -- cgit v1.2.3