diff options
Diffstat (limited to 'src/wavegen.c')
-rw-r--r-- | src/wavegen.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/src/wavegen.c b/src/wavegen.c new file mode 100644 index 0000000..c722e04 --- /dev/null +++ b/src/wavegen.c @@ -0,0 +1,310 @@ +/* + A program to generate c header files containing pre-calculated wavedata. + Copyright 2011 David Robillard + Copyright 2002 Mike Rawes + + This is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this software. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <getopt.h> +#include "wdatutil.h" +#include "wavedata.h" +#include "common.h" + +static void +usage(void) +{ + int i; + + fprintf(stderr, "\n"); + fprintf(stderr, "Generate bandlimited wavedata and write as c header file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: wavegen -w <Wavename> -r <Sample Rate> -f <Note> -s <Note Step>\n"); + fprintf(stderr, " -m <Samples> [-o <Output Filename>] [-p <Prefix>]\n"); + fprintf(stderr, " [-g <Factor>] [-q] [-t] [-h]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " -w, --wave Name of wave to generate (case insensitive)\n"); + fprintf(stderr, " -r, --rate Intended playback rate in Samples/Second\n"); + fprintf(stderr, " -f, --first First MIDI note to generate table for\n"); + fprintf(stderr, " -s, --step Number of MIDI notes to skip for next table\n"); + fprintf(stderr, " -m, --min Minimum table size in samples\n"); + fprintf(stderr, " -o, --output Output Filename, name of file to output\n"); + fprintf(stderr, " If not given, output is to stdout\n"); + fprintf(stderr, " -p, --prefix Prefix for declarations in header\n"); + fprintf(stderr, " -g, --gibbs Compensate for Gibbs' effect\n"); + fprintf(stderr, " -q, --quiet Surpress stderr output\n"); + fprintf(stderr, " -t, --test Don't actually generate data\n"); + fprintf(stderr, " -h, --help Print this text and exit\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Supported waves:\n"); + + for (i = 0; i < WAVE_TYPE_COUNT; i++) { + fprintf(stderr, " %s (%s)\n", wave_names[i], wave_descriptions[i]); + } + + fprintf(stderr, "\n"); + fprintf(stderr, "Gibbs' Effect\n"); + fprintf(stderr, " Gibbs' effect causes overshoot in waves generated from finite\n"); + fprintf(stderr, " Fourier Series. Compensation can be applied, which will result in\n"); + fprintf(stderr, " a waveform that sounds slightly less bright.\n"); + fprintf(stderr, " Use the --gibbs option to set degree of compensatation, from 0.0\n"); + fprintf(stderr, " (no compensation) to 1.0 (full compensation)\n"); + fprintf(stderr, "\n"); +} + +/** + Create bandlimited wavedata header files for various + waveforms +*/ +int +main(int argc, + char** argv) +{ + int option_index; + int opt; + const char* options = "w:r:f:s:m:o:p:g:qth"; + struct option long_options[] = { + { "wave", 1, 0, 'w' }, + { "rate", 1, 0, 'r' }, + { "first", 1, 0, 'f' }, + { "step", 1, 0, 's' }, + { "min", 1, 0, 'm' }, + { "output", 1, 0, 'o' }, + { "prefix", 0, 0, 'p' }, + { "gibbs", 1, 0, 'g' }, + { "quiet", 0, 0, 'q' }, + { "test", 0, 0, 't' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } + }; + int wavetype = -1; + long sample_rate = -1; + long first_note = -1; + long note_step = -1; + long min_table_size = -1; + const char* filename = NULL; + FILE* file; + const char* prefix = NULL; + float gibbs = 0.0f; + int quiet = 0; + int test = 0; + + Wavedata* w; + float freq; + uint32_t sample_count; + unsigned long max_harmonic_hf; + unsigned long max_harmonic_lf; + unsigned long i; + + size_t strcmplen; + size_t len1; + size_t len2; + + /* Parse arguments */ + if (argc == 1) { + usage(); + exit(-1); + } + + opterr = 0; + while ((opt = getopt_long(argc, argv, options, long_options, &option_index)) != -1) { + switch (opt) { + case 'w': + for (i = 0; i < WAVE_TYPE_COUNT; i++) { + len1 = strlen(optarg); + len2 = strlen(wave_names[i]); + strcmplen = len1 < len2 ? len1 : len2; + + if (!strncmp(optarg, wave_names[i], strcmplen)) { + wavetype = i; + } + } + if (wavetype == -1) { + fprintf(stderr, "Unrecognised option for Wave: %s\n", optarg); + exit(-1); + } + break; + case 'r': + sample_rate = (long)atoi(optarg); + break; + case 'f': + first_note = (long)atoi(optarg); + break; + case 's': + note_step = (long)atoi(optarg); + break; + case 'm': + min_table_size = (long)atoi(optarg); + break; + case 'o': + filename = optarg; + break; + case 'p': + prefix = optarg; + break; + case 'g': + gibbs = atof(optarg); + break; + case 'q': + quiet = -1; + break; + case 't': + test = -1; + break; + case 'h': + usage(); + exit(0); + break; + default: + usage(); + exit(-1); + } + } + + /* Check basic arguments */ + if (wavetype == -1) { + if (!quiet) { + fprintf(stderr, "No wavetype specified.\n"); + } + exit(-1); + } + + if (sample_rate == -1) { + if (!quiet) { + fprintf(stderr, "No sample rate specified.\n"); + } + exit(-1); + } + + if (first_note == -1) { + if (!quiet) { + fprintf(stderr, "No first note specified.\n"); + } + exit(-1); + } + + if (note_step == -1) { + if (!quiet) { + fprintf(stderr, "No note step specified.\n"); + } + exit(-1); + } + + if (min_table_size == -1) { + if (!quiet) { + fprintf(stderr, "No minimum table size specified.\n"); + } + exit(-1); + } + + if (gibbs < 0.0f || gibbs > 1.0f) { + if (!quiet) { + fprintf(stderr, "Gibbs compensation clamped to [0.0, 1.0]\n"); + fprintf(stderr, " Supplied value: %.2f\n", gibbs); + } + gibbs = gibbs < 0.0f ? 0.0f : gibbs; + gibbs = gibbs > 1.0f ? 1.0f : gibbs; + if (!quiet) { + fprintf(stderr, " Clamped to: %.2f\n", gibbs); + } + } + + if (note_step < 1) { + if (!quiet) { + fprintf(stderr, "Using minimum note step of 1\n"); + } + note_step = 1; + } + + /* Get file to write to */ + if (!filename) { + file = stdout; + } else { + file = fopen(filename, "w"); + } + + w = wavedata_new(sample_rate); + + if (!w) { + if (!quiet) { + fprintf(stderr, "Unable to create wavedata\n"); + } + + exit(-1); + } + + freq = FREQ_FROM_NOTE(first_note); + max_harmonic_lf = HARM_FROM_FREQ(freq, sample_rate); + max_harmonic_hf = max_harmonic_lf; + + for (i = 0; max_harmonic_hf > MIN_HARM(wavetype); i += note_step) { + freq = FREQ_FROM_NOTE(first_note + i + note_step); + max_harmonic_hf = HARM_FROM_FREQ(freq, sample_rate); + + max_harmonic_hf = ACTUAL_HARM(max_harmonic_hf, wavetype); + max_harmonic_lf = ACTUAL_HARM(max_harmonic_lf, wavetype); + + while (max_harmonic_lf == max_harmonic_hf) { + i += note_step; + freq = FREQ_FROM_NOTE(first_note + i + note_step); + max_harmonic_hf = HARM_FROM_FREQ(freq, sample_rate); + max_harmonic_hf = ACTUAL_HARM(max_harmonic_hf, wavetype); + } + + if (max_harmonic_lf > MIN_EXTRA_HARM(wavetype)) { + sample_count = max_harmonic_lf * 2; + sample_count = sample_count < min_table_size ? min_table_size : sample_count; + + if (wavedata_add_table(w, sample_count, max_harmonic_lf)) { + if (!quiet) { + fprintf(stderr, "Could not add wavetable to wavedata\n"); + } + + wavedata_cleanup(w); + exit(-1); + } + } + max_harmonic_lf = max_harmonic_hf; + } + + if (!quiet) { + fprintf(stderr, "\n"); + fprintf(stderr, "Generating %s wave\n", wave_names[wavetype]); + fprintf(stderr, " Sample Rate: %ld\n", sample_rate); + if (gibbs > 0.0f) { + fprintf(stderr, " Gibbs' compensation factor: %+.2f\n\n", gibbs); + } + } + + wavedata_generate_tables(w, (Wavetype)wavetype, gibbs); + + if (!test) { + if (wavedata_write(w, file, prefix)) { + if (!quiet) { + fprintf(stderr, "Could not write to file %s!\n\n", filename); + } + } else { + if (!quiet) { + fprintf(stderr, "Written to file %s\n\n", filename); + } + } + } + + wavedata_cleanup(w); + + return 0; +} |