diff options
author | Richard <q@1bpm.net> | 2025-03-08 14:53:52 +0000 |
---|---|---|
committer | Richard <q@1bpm.net> | 2025-03-08 14:53:52 +0000 |
commit | d8baa01ff91521e113260ef5d5cae272e02162e2 (patch) | |
tree | 6b118c71c308d29e517bda60bfbd69f7c4f39cbb /examples | |
download | libguttersynth-d8baa01ff91521e113260ef5d5cae272e02162e2.tar.gz libguttersynth-d8baa01ff91521e113260ef5d5cae272e02162e2.tar.bz2 libguttersynth-d8baa01ff91521e113260ef5d5cae272e02162e2.zip |
initial
Diffstat (limited to 'examples')
-rwxr-xr-x | examples/build | 5 | ||||
-rw-r--r-- | examples/example1.c | 226 | ||||
-rw-r--r-- | examples/example2.cpp | 120 | ||||
-rw-r--r-- | examples/example3.c | 218 |
4 files changed, 569 insertions, 0 deletions
diff --git a/examples/build b/examples/build new file mode 100755 index 0000000..d5d5ece --- /dev/null +++ b/examples/build @@ -0,0 +1,5 @@ +# these will be built automatically by cmake, so usually no need to run this + +gcc -lguttersynth example1.c -oexample1 +g++ -lguttersynth example2.cpp -oexample2 +gcc -lguttersynth -lportaudio example3.c -oexample3 diff --git a/examples/example1.c b/examples/example1.c new file mode 100644 index 0000000..f827ca7 --- /dev/null +++ b/examples/example1.c @@ -0,0 +1,226 @@ +#include <stdio.h> +#include <stdlib.h> +#include <float.h> +#include <time.h> +#include <limits.h> +#include <string.h> +#include <math.h> +#include "guttersynth.h" + +int samples_per_second = 44100; +int bits_per_sample = 16; +char* output_name = "guttersynth-example1.wav"; + +int written_samples = 0; + +struct wavfile_header { + char riff_tag[4]; + int riff_length; + char wave_tag[4]; + char fmt_tag[4]; + int fmt_length; + short audio_format; + short num_channels; + int sample_rate; + int byte_rate; + short block_align; + short bits_per_sample; + char data_tag[4]; + int data_length; +}; + + +FILE* target; + +/* + * open wavfile and set the riff header + */ +FILE* wavfile_open() +{ + // populate the header + struct wavfile_header header; + strncpy(header.riff_tag, "RIFF",4); + strncpy(header.wave_tag, "WAVE",4); + strncpy(header.fmt_tag, "fmt ",4); + strncpy(header.data_tag, "data",4); + header.riff_length = 0; + header.fmt_length = 16; + header.audio_format = 1; + header.num_channels = 1; + header.sample_rate = samples_per_second; + header.byte_rate = samples_per_second * (bits_per_sample / 8); + header.block_align = bits_per_sample/8; + header.bits_per_sample = bits_per_sample; + header.data_length = 0; + + FILE *file = fopen(output_name, "w+"); + if(!file) return 0; + fwrite(&header, sizeof(header), 1, file); + fflush(file); + + return file; +} + + +/* + * write to wavfile + */ +void wavfile_write(double* data, int length) +{ + short* shortdata = (short*) malloc(sizeof(short) * length); + int i; + double sample; + for (i = 0; i < length; i++) { + sample = data[i] * 0.5; /// (DBL_MAX*0.01); + shortdata[i] = (short) (sample * SHRT_MAX * 0.5); + } + fwrite(shortdata, sizeof(short), length, target); + fflush(target); + written_samples += length; + free(shortdata); +} + + +/* + * close the open wavfile + */ +void wavfile_close() +{ + int file_length = ftell(target); + int data_length = file_length - sizeof(struct wavfile_header); + fseek(target, sizeof(struct wavfile_header) - sizeof(int), SEEK_SET); + fwrite(&data_length, sizeof(data_length), 1, target); + int riff_length = file_length - 8; + fseek(target, 4, SEEK_SET); + fwrite(&riff_length, sizeof(riff_length), 1, target); + fclose(target); +} + +/* + * Returns a random float within a range + * + * min: bottom end of range + * max: top end of range + * + * returns: random float + */ +double rangerandom(double min, double max) +{ + double random = ((double) rand()) / (double) RAND_MAX; + double diff = max - min; + double r = random * diff; + return min + r; +} + +int boolrandom() +{ + return (rand() > (RAND_MAX / 2)); +} + +double interpolate(double items[], int points, int point) +{ + return (((items[1] - items[0]) / points) * point) + items[0]; +} + +void create() +{ + target = wavfile_open(); + int seconds = 20; + int total_samples = samples_per_second * seconds; + int sample = 0; + int buffer_pos = 0; + int buffer_size = 4410; + double *buffer = (double*) malloc(sizeof(double) * buffer_size); + + gutter_state *gs = gutter_init(4, 24, samples_per_second); + //gutter_randomisefilters(gs); + + double* filterAutomation = malloc(sizeof(double) * gs->bankCount * gs->filterCount * 3 * 2); + + int b, f, i, ai; + double val; + for (b = 0, ai=0; b < gs->bankCount; b++) { + for (f = 0; f < gs->filterCount; f++) { + i = b * gs->bankCount + f; + val = rangerandom(100, 2000); + filterAutomation[ai++] = val; + filterAutomation[ai++] = (boolrandom()) ? val : rangerandom(100, 2000); + + val = rangerandom(0.3, 1); + filterAutomation[ai++] = val; + filterAutomation[ai++] = (boolrandom()) ? val : rangerandom(0.3, 1); + + val = rangerandom(20, 200); + filterAutomation[ai++] = val; + filterAutomation[ai++] = (boolrandom()) ? val : rangerandom(20, 200); + } + } + + + + double gamma[2] = { rangerandom(2, 10), rangerandom(2, 10) }; + double omega[2] = { rangerandom(0.2, 1), rangerandom(0.2, 1) }; + double c[2] = { rangerandom(0.2, 2), rangerandom(0.2, 2) }; + double dt[2] = { rangerandom(100, 10000), rangerandom(100, 10000) }; + double singleGain[2] = { rangerandom(0.2, 0.7), rangerandom(0.2, 0.7) }; + + + gs->filtersOn = 1; + gs->smoothing = 1; + gs->distortionMethod = 2; + + gs->omega = rangerandom(0.3, 0.9); + gs->c = rangerandom(0.5, 0.9); + gs->dt = rangerandom(100, 500); + + gutter_randomisefilters(gs); + + for (sample = 0; sample <= total_samples; sample ++) { + gs->gamma = interpolate(gamma, total_samples, sample); + //gs->omega = 0.8; //interpolate(omega, total_samples, sample); + //gs->c = 0.8; //interpolate(c, total_samples, sample); + //gs->dt = 300; //interpolate(dt, total_samples, sample); + gs->singleGain = 0.8; //interpolate(singleGain, total_samples, sample); + + + for (b = 0, ai = 0; b < gs->bankCount; b++) { + for (f = 0; f < gs->filterCount; f++, ai=ai+6) { + i = b * gs->bankCount + f; + gs->filterFreqs[i] = interpolate(filterAutomation + ai, total_samples, sample); + gs->gains[i] = interpolate(filterAutomation + ai + 2, total_samples, sample); + gs->Q[i] = interpolate(filterAutomation + ai + 4, total_samples, sample); + } + } + gutter_calccoeffs(gs); + + buffer[buffer_pos] = gutter_process(gs); + if (buffer_pos == buffer_size - 1) { + buffer_pos = 0; + wavfile_write(buffer, buffer_size); + } else { + buffer_pos ++; + } + + // randomise filters every second + if (sample % samples_per_second == 0) { + //gutter_randomisefilters(gs); + } + } + + free(buffer); + free(filterAutomation); + gutter_cleanup(gs); + wavfile_close(); +} + + +int main() +{ + // random seed from time + srand((unsigned int) time(NULL)); + + // generate the file + create(); + + return 0; +} diff --git a/examples/example2.cpp b/examples/example2.cpp new file mode 100644 index 0000000..415c1ff --- /dev/null +++ b/examples/example2.cpp @@ -0,0 +1,120 @@ +#include <cstdlib> +#include <fstream> +#include <iostream> +#include "guttersynth.hpp" + +using namespace std; + + +class Wavefile { +private: + std::ofstream output; + size_t data_chunk_pos; + + template <typename Word> + void write_word(Word value, unsigned size=sizeof(Word)) { + for (; size; --size, value >>= 8) + output.put( static_cast <char> (value & 0xFF) ); + } + +public: + Wavefile(string path, int samplerate) : output(path.c_str(), ios::binary) { + + int bitdepth = 16; + // Write the file headers + output << "RIFF----WAVEfmt "; // (chunk size to be filled in later) + write_word(16, 4); // no extension data + write_word(1, 2); // PCM - integer samples + write_word(1, 2); // one channel (mono file) + write_word(samplerate, 4); // samples per second (Hz) + write_word(samplerate * (bitdepth / 8), 4); // byte rate + write_word(2, 2); // data block size ; was bitdepth / 8 + write_word(bitdepth, 2); // number of bits per sample (use a multiple of 8) + + data_chunk_pos = output.tellp(); + output << "data----"; // (chunk size to be filled in later) + } + + ~Wavefile() { + std::cout << "closing\n"; + size_t file_length = output.tellp(); + + // Fix the data chunk header to contain the data size + output.seekp(data_chunk_pos + 4); + write_word(file_length - (data_chunk_pos + 8), 4); + + // Fix the file header to contain the proper RIFF chunk size, which is (file size - 8) bytes + output.seekp(0 + 4); + write_word(file_length - 8, 4); + output.close(); + } + + void write(double value) { + //https://www.cplusplus.com/forum/beginner/166954/ + write_word((short) (value * SHRT_MAX * 0.5), 2); + } + + +}; + + +class Example2 { +private: + GutterSynth* gs; + Wavefile* wav; + int samplerate; + +public: + + Example2(std::string path, int samplerate) { + this->samplerate = samplerate; + gs = new GutterSynth(2, 24, samplerate); + wav = new Wavefile(path, samplerate); + } + + double rangerandom(double min, double max) { + double random = ((double) rand()) / (double) RAND_MAX; + double diff = max - min; + double r = random * diff; + return min + r; + } + + void run() { + int total_samples = 30 * samplerate; + int sample = 0; + short data; + gs->randomiseFilters(); + gs->filtersOn(true); + gs->smoothing(true); + gs->distortionMethod(2); + + for (int sample = 0; sample < total_samples; sample ++) { + data = gs->process(); + if (data > 0) { + std::cout << data; + } + wav->write(data); + + if (sample % samplerate == 0) { // randomise every second + gs->randomiseFilters(); + gs->gamma(rangerandom(2, 10)); + gs->omega(rangerandom(0.3, 0.9)); + gs->c(rangerandom(0.5, 0.9)); + gs->dt(rangerandom(100, 500)); + gs->singleGain(rangerandom(0.2, 0.7)); + } + } + delete wav; + } +}; + + +/* + * + */ +int main(int argc, char** argv) { + Example2* e = new Example2("guttersynth-example2.wav", 44100); + e->run(); + return 0; +} + diff --git a/examples/example3.c b/examples/example3.c new file mode 100644 index 0000000..ce2d116 --- /dev/null +++ b/examples/example3.c @@ -0,0 +1,218 @@ +/* + * Example 3: randomised realtime output using Portaudio + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <limits.h> +#include <string.h> +#include <math.h> +#include "guttersynth.h" +#include "portaudio.h" + +#define NUM_SECONDS 60 +#define SAMPLE_RATE 44100 +#define FRAMES_PER_BUFFER 128 + + + +typedef struct { + gutter_state *gs; + long sample; + long total_samples; + double gamma[2]; + double omega[2]; + double c[2]; + double dt[2]; + double singleGain[2]; + double* filterAutomation; +} playstate; + + + +/* + * Returns a random float within a range + * + * min: bottom end of range + * max: top end of range + * + * returns: random float + */ +double rangerandom(double min, double max) +{ + double random = ((double) rand()) / (double) RAND_MAX; + double diff = max - min; + double r = random * diff; + return min + r; +} + + + + +double interpolate(double* items, int points, int point) +{ + return (((items[1] - items[0]) / points) * point) + items[0]; +} + + + +void rerandom(playstate* ps) { + ps->gamma[0] = rangerandom(0.1, 10); + ps->gamma[1] = rangerandom(0.1, 10); + ps->omega[0] = rangerandom(0.01, 2); + ps->omega[1] = rangerandom(0.01, 2); + ps->c[0] = rangerandom(0.2, 2); + ps->c[1] = rangerandom(0.2, 2); + ps->dt[0] = rangerandom(400, 4000); + ps->dt[1] = rangerandom(400, 4000); + ps->singleGain[0] = rangerandom(0.2, 0.3); + ps->singleGain[1] = rangerandom(0.2, 0.3); +} + +static int audiocallback(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + + unsigned long i, sample; + float audioSample; + float *out = (float*)outputBuffer; + playstate *ps = (playstate*) userData; + + gutter_state *gs = ps->gs; + + for (i = 0; i < framesPerBuffer; i++, ps->sample++) { + if (ps->sample % SAMPLE_RATE == 0) { + //gutter_randomisefilters(gs); + //rerandom(ps); + } + + int b, f, ix, ai; + for (b = 0, ai = 0; b < gs->bankCount; b++) { + for (f = 0; f < gs->filterCount; f++, ai=ai+6) { + ix = b * gs->bankCount + f; + gs->filterFreqs[ix] = interpolate(ps->filterAutomation + ai, ps->total_samples, ps->sample); + gs->gains[ix] = interpolate(ps->filterAutomation + ai + 2, ps->total_samples, ps->sample); + gs->Q[ix] = interpolate(ps->filterAutomation + ai + 4, ps->total_samples, ps->sample); + } + } + gutter_calccoeffs(gs); + + gs->gamma = 7; //interpolate(ps->gamma, ps->total_samples, ps->sample); + gs->omega = 1.2; //interpolate(ps->omega, ps->total_samples, ps->sample); + gs->c = 0.5; //interpolate(ps->c, ps->total_samples, ps->sample); + gs->dt = 500; //interpolate(ps->dt, ps->total_samples, ps->sample); + gs->singleGain = 1; //interpolate(ps->singleGain, ps->total_samples, ps->sample); + + audioSample = (float) gutter_process(gs); + *out++ = audioSample * 0.2; + *out++ = audioSample * 0.2; + } + //ps->sample = ps->sample + framesPerBuffer; + (void) timeInfo; /* Prevent unused variable warnings. */ + (void) statusFlags; + (void) inputBuffer; + + return paContinue; +} + + + + +int main() +{ + // random seed from time + srand((unsigned int) time(NULL)); + + + playstate ps; + ps.sample = 0; + ps.total_samples = SAMPLE_RATE * NUM_SECONDS; + ps.gs = gutter_init(2, 32, SAMPLE_RATE); + ps.gs->filtersOn = 1; + ps.gs->smoothing = 1; + ps.gs->distortionMethod = 2; + + rerandom(&ps); + + + ps.filterAutomation = malloc(sizeof(double) * ps.gs->bankCount * ps.gs->filterCount * 3 * 2); + int b, f, i, ai; + for (b = 0, ai=0; b < ps.gs->bankCount; b++) { + for (f = 0; f < ps.gs->filterCount; f++) { + i = b * ps.gs->bankCount + f; + ps.filterAutomation[ai++] = rangerandom(100, 2000); + ps.filterAutomation[ai++] = rangerandom(100, 2000); + ps.filterAutomation[ai++] = rangerandom(0.6, 1); + ps.filterAutomation[ai++] = rangerandom(0.6, 1); + ps.filterAutomation[ai++] = rangerandom(20, 200); + ps.filterAutomation[ai++] = rangerandom(20, 200); + } + } + + gutter_randomisefilters(ps.gs); + + PaStreamParameters outputParameters; + PaStream *stream; + PaError err; + + err = Pa_Initialize(); + if (err != paNoError) { + goto error; + } + + outputParameters.device = Pa_GetDefaultOutputDevice(); + if (outputParameters.device == paNoDevice) { + fprintf(stderr,"Error: No default output device.\n"); + goto error; + } + + outputParameters.channelCount = 2; /* stereo output */ + outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( + &stream, + NULL, /* no input */ + &outputParameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + audiocallback, + &ps); + + if (err != paNoError) goto error; + + //err = Pa_SetStreamFinishedCallback(stream, &StreamFinished); + //if(err != paNoError) goto error; + + err = Pa_StartStream(stream); + if(err != paNoError) goto error; + + printf("Play for %d seconds.\n", NUM_SECONDS); + Pa_Sleep(NUM_SECONDS * 1000); + + err = Pa_StopStream( stream ); + if(err != paNoError) goto error; + + err = Pa_CloseStream(stream); + if(err != paNoError) goto error; + + Pa_Terminate(); + printf("Example finished.\n"); + + free(ps.filterAutomation); + + return err; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} |