summaryrefslogtreecommitdiffstats
path: root/src/synth_engine_v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/synth_engine_v2.c')
-rw-r--r--src/synth_engine_v2.c83
1 files changed, 68 insertions, 15 deletions
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 <stdint.h> // 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;