/* * lingot, a musical instrument tuner. * * Copyright (C) 2004-2011 Ibán Cereijo Graña, Jairo Chapela Martínez. * * This file is part of lingot. * * lingot 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 2 of the License, or * (at your option) any later version. * * lingot 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 lingot; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <string.h> #include <stdlib.h> #include <stdio.h> #include <math.h> #include <locale.h> #include "lingot-defs.h" #include "lingot-config.h" #include "lingot-config-scale.h" #include "lingot-msg.h" #include "lingot-i18n.h" #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define N_OPTIONS 20 // the following tokens will appear in the config file. The options after | are deprecated options. char* options[] = { "AUDIO_SYSTEM", "AUDIO_DEV", "AUDIO_DEV_ALSA", "SAMPLE_RATE", "OVERSAMPLING", "ROOT_FREQUENCY_ERROR", "MIN_FREQUENCY", "FFT_SIZE", "TEMPORAL_WINDOW", "NOISE_THRESHOLD", "CALCULATION_RATE", "VISUALIZATION_RATE", "PEAK_NUMBER", "PEAK_HALF_WIDTH", "PEAK_REJECTION_RELATION", "DFT_NUMBER", "DFT_SIZE", "GAIN", "|", "PEAK_ORDER", NULL // NULL terminated array }; // print/scan param formats. const char* option_formats = "mssddffdffffddfddf|d"; // converts an audio_system_t to a string const char* audio_system_t_to_str(audio_system_t audio_system) { const char* values[] = { "OSS", "ALSA", "JACK", "PORTAUDIO" }; return values[audio_system]; } // converts a string to an audio_system_t audio_system_t str_to_audio_system_t(char* audio_system) { audio_system_t result = -1; const char* values[] = { "OSS", "ALSA", "JACK", "PORTAUDIO", NULL }; int i; for (i = 0; values[i] != NULL; i++) { if (!strcmp(audio_system, values[i])) { result = i; break; } } return result; } //---------------------------------------------------------------------------- LingotConfig* lingot_config_new() { LingotConfig* config = malloc(sizeof(LingotConfig)); config->max_nr_iter = 10; // iterations config->window_type = HAMMING; config->scale = lingot_config_scale_new(); return config; } void lingot_config_destroy(LingotConfig* config) { lingot_config_scale_destroy(config->scale); free(config->scale); free(config); } void lingot_config_copy(LingotConfig* dst, LingotConfig* src) { LingotScale* dst_scale = dst->scale; *dst = *src; dst->scale = dst_scale; lingot_config_scale_copy(dst->scale, src->scale); } //---------------------------------------------------------------------------- void lingot_config_restore_default_values(LingotConfig* config) { # ifndef ALSA config->audio_system = AUDIO_SYSTEM_PORTAUDIO; # else config->audio_system = AUDIO_SYSTEM_ALSA; # endif sprintf(config->audio_dev[AUDIO_SYSTEM_OSS], "%s", "/dev/dsp"); sprintf(config->audio_dev[AUDIO_SYSTEM_ALSA], "%s", "plughw:0"); sprintf(config->audio_dev[AUDIO_SYSTEM_PORTAUDIO], "%s", ""); config->sample_rate = 44100; // Hz config->oversampling = 25; config->root_frequency_error = 0; // Hz config->min_frequency = 15; // Hz config->fft_size = 512; // samples config->temporal_window = 0.32; // seconds config->calculation_rate = 20; // Hz config->visualization_rate = 30; // Hz config->noise_threshold_db = 20.0; // dB config->gain = 0; config->peak_number = 3; // peaks config->peak_half_width = 1; // samples config->peak_rejection_relation_db = 20; // dB config->dft_number = 2; // DFTs config->dft_size = 15; // samples //-------------------------------------------------------------------------- lingot_config_scale_restore_default_values(config->scale); lingot_config_update_internal_params(config); } //---------------------------------------------------------------------------- void lingot_config_update_internal_params(LingotConfig* config) { // derived parameters. config->temporal_buffer_size = (unsigned int) ceil(config->temporal_window * config->sample_rate / config->oversampling); config->peak_rejection_relation_nu = pow(10.0, config->peak_rejection_relation_db / 10.0); config->noise_threshold_nu = pow(10.0, config->noise_threshold_db / 10.0); config->gain_nu = pow(10.0, config->gain / 20.0); LingotScale* scale = config->scale; if (scale->notes == 1) { scale->max_offset_rounded = 1200.0; } else { int i; FLT max_offset = 0.0; for (i = 1; i < scale->notes; i++) { max_offset = MAX(max_offset, scale->offset_cents[i] - scale->offset_cents[i - 1]); } scale->max_offset_rounded = max_offset; } config->gauge_rest_value = -0.45 * scale->max_offset_rounded; sprintf(config->audio_dev[AUDIO_SYSTEM_JACK], "%s", ""); } //---------------------------------------------------------------------------- // internal parameters mapped to each token in the config file. void lingot_map_parameters(LingotConfig* config, void* params[]) { void* c_params[] = { &config->audio_system, &config->audio_dev[AUDIO_SYSTEM_OSS], &config->audio_dev[AUDIO_SYSTEM_ALSA], &config->sample_rate, &config->oversampling, &config->root_frequency_error, &config->min_frequency, &config->fft_size, &config->temporal_window, &config->noise_threshold_db, &config->calculation_rate, &config->visualization_rate, &config->peak_number, &config->peak_half_width, &config->peak_rejection_relation_db, &config->dft_number, &config->dft_size, &config->gain, NULL, &config->peak_half_width }; memcpy(params, c_params, N_OPTIONS * sizeof(void*)); } void lingot_config_save(LingotConfig* config, char* filename) { unsigned int i; FILE* fp; char* lc_all; void* params[N_OPTIONS]; // parameter pointer array. void* param = NULL; char* option = NULL; char buff[80]; lingot_map_parameters(config, params); lc_all = setlocale(LC_ALL, NULL); // duplicate the string, as the next call to setlocale will destroy it if (lc_all) lc_all = strdup(lc_all); setlocale(LC_ALL, "C"); if ((fp = fopen(filename, "w")) == NULL) { char buff[100]; sprintf(buff, "error saving config file %s ", filename); perror(buff); return; } fprintf(fp, "# Config file automatically created by lingot %s\n\n", VERSION); for (i = 0; strcmp(options[i], "|"); i++) { option = options[i]; param = params[i]; switch (option_formats[i]) { case 's': fprintf(fp, "%s = %s\n", option, (char*) param); break; case 'd': fprintf(fp, "%s = %d\n", option, *((unsigned int*) param)); break; case 'f': fprintf(fp, "%s = %0.3f\n", option, *((FLT*) param)); break; case 'm': if (!strcmp("AUDIO_SYSTEM", option)) { fprintf(fp, "%s = %s\n", option, audio_system_t_to_str( *((audio_system_t*) param))); } break; } } fprintf(fp, "\n"); fprintf(fp, "SCALE = {\n"); fprintf(fp, "NAME = %s\n", config->scale->name); fprintf(fp, "BASE_FREQUENCY = %f\n", config->scale->base_frequency); fprintf(fp, "NOTE_COUNT = %d\n", config->scale->notes); fprintf(fp, "NOTES = {\n"); for (i = 0; i < config->scale->notes; i++) { lingot_config_scale_format_shift(buff, config->scale->offset_cents[i], config->scale->offset_ratios[0][i], config->scale->offset_ratios[1][i]); fprintf(fp, "%s\t%s\n", config->scale->note_name[i], buff); } fprintf(fp, "}\n"), fprintf(fp, "}\n"), fclose(fp); if (lc_all) { setlocale(LC_ALL, lc_all); free(lc_all); } } //---------------------------------------------------------------------------- void lingot_config_load(LingotConfig* config, char* filename) { FILE* fp; float aux; int line; int option_index; int deprecated_option = 0; char* char_buffer_pointer; const static char* delim = " \t=\n"; const static char* delim2 = " \t\n"; void* params[N_OPTIONS]; // parameter pointer array. void* param = NULL; char* option = NULL; int reading_scale = 0; char* nl; int parse_errors = 0; int command_count = 0; // restore default values for non specified parameters lingot_config_restore_default_values(config); lingot_map_parameters(config, params); # define MAX_LINE_SIZE 100 char char_buffer[MAX_LINE_SIZE]; if ((fp = fopen(filename, "r")) == NULL) { sprintf(char_buffer, "error opening config file %s, assuming default values ", filename); perror(char_buffer); return; } line = 0; for (;;) { line++; if (!fgets(char_buffer, MAX_LINE_SIZE, fp)) break;; if (char_buffer[0] == '#') continue; // tokens into the line. char_buffer_pointer = strtok(char_buffer, delim); if (!char_buffer_pointer) continue; // blank line. if (!strcmp(char_buffer_pointer, "SCALE")) { reading_scale = 1; config->scale = lingot_config_scale_new(); command_count++; continue; } if (reading_scale) { if (!strcmp(char_buffer_pointer, "NAME")) { char_buffer_pointer += 4; while (1) { nl = strchr(delim, *char_buffer_pointer); if (!nl) break; char_buffer_pointer++; } nl = strrchr(char_buffer_pointer, '\r'); if (nl) *nl = '\0'; nl = strrchr(char_buffer_pointer, '\n'); if (nl) *nl = '\0'; config->scale->name = strdup(char_buffer_pointer); continue; } if (!strcmp(char_buffer_pointer, "BASE_FREQUENCY")) { char_buffer_pointer = strtok(NULL, delim); sscanf(char_buffer_pointer, "%lg", &config->scale->base_frequency); continue; } if (!strcmp(char_buffer_pointer, "NOTE_COUNT")) { char_buffer_pointer = strtok(NULL, delim); sscanf(char_buffer_pointer, "%hu", &config->scale->notes); lingot_config_scale_allocate(config->scale, config->scale->notes); continue; } if (!strcmp(char_buffer_pointer, "NOTES")) { int i = 0; for (i = 0; i < config->scale->notes; i++) { line++; if (!fgets(char_buffer, MAX_LINE_SIZE, fp)) break; // tokens into the line. char_buffer_pointer = strtok(char_buffer, delim2); config->scale->note_name[i] = strdup(char_buffer_pointer); char_buffer_pointer = strtok(NULL, delim2); if (!lingot_config_scale_parse_shift(char_buffer_pointer, &config->scale->offset_cents[i], &config->scale->offset_ratios[0][i], &config->scale->offset_ratios[1][i])) { parse_errors = 1; } } line++; if (!fgets(char_buffer, MAX_LINE_SIZE, fp)) break; // } continue; } if (!strcmp(char_buffer_pointer, "}")) { reading_scale = 0; continue; } } deprecated_option = 0; for (option_index = 0; options[option_index]; option_index++) { if (!strcmp(char_buffer_pointer, options[option_index])) { break; // found token. } else if (!strcmp("|", options[option_index])) { deprecated_option = 1; } } option = options[option_index]; param = params[option_index]; if (!option) { fprintf(stderr, "warning: parse error at line %i: unknown keyword %s\n", line, char_buffer_pointer); parse_errors = 1; continue; } if (deprecated_option) { fprintf(stdout, "warning: deprecated option %s\n", char_buffer_pointer); } // take the attribute value. char_buffer_pointer = strtok(NULL, delim); if (!char_buffer_pointer) { fprintf(stderr, "warning: parse error at line %i: value expected\n", line); parse_errors = 1; continue; } // asign the value to the parameter. switch (option_formats[option_index]) { case 's': sprintf(((char*) param), "%s", char_buffer_pointer); command_count++; break; case 'd': sscanf(char_buffer_pointer, "%d", (unsigned int*) param); command_count++; break; case 'f': sscanf(char_buffer_pointer, "%f", &aux); *((FLT*) param) = aux; command_count++; break; case 'm': if (!strcmp("AUDIO_SYSTEM", option)) { command_count++; *((audio_system_t*) param) = str_to_audio_system_t( char_buffer_pointer); if (*((audio_system_t*) param) == (audio_system_t) -1) { # ifndef ALSA *((audio_system_t*) param) = AUDIO_SYSTEM_PORTAUDIO; # else *((audio_system_t*) param) = AUDIO_SYSTEM_ALSA; # endif char buff[1000]; sprintf( buff, _( "Error parsing the configuration file, line %i: unrecognized audio system '%s', assuming default audio system.\n"), line, char_buffer_pointer); lingot_msg_add_warning(buff); parse_errors = 1; } } break; } } fclose(fp); if (parse_errors) { lingot_msg_add_warning( _( "The configuration file contains errors, and hence some default values have been chosen. Consider checking the settings and fixing the problem using the configuration dialog.")); } lingot_config_update_internal_params(config); # undef MAX_LINE_SIZE }