diff options
| author | Piotr Pawlow <pp@siedziba.pl> | 2014-03-13 20:51:31 +0100 |
|---|---|---|
| committer | Piotr Pawlow <pp@siedziba.pl> | 2014-03-13 20:51:31 +0100 |
| commit | c04af18902322cea1bd05b6f01b7860ca7bfa432 (patch) | |
| tree | d8d3f812112c0cc835fd29e68d92e297e0164b52 /src | |
- import version 0.9.1 from upstream
Diffstat (limited to 'src')
41 files changed, 8192 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..03b32d1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,55 @@ +SUBDIRS = glade + +INCLUDES = \ + $(PACKAGE_CFLAGS) $(GTK_CFLAGS) $(ALSA_CFLAGS) $(JACK_CFLAGS) $(GLADE_CFLAGS)\ + -DLINGOT_GLADEDIR=\""$(datadir)/lingot/glade/"\" \ + -DLINGOT_LOCALEDIR=\""$(datadir)/locale"\" + +AM_CXXFLAGS =\ + -Wall + +bin_PROGRAMS = lingot + +lingot_SOURCES = \ + lingot-fft.c\ + lingot-fft.h\ + lingot-audio.c\ + lingot-audio.h\ + lingot-audio-oss.c\ + lingot-audio-oss.h\ + lingot-audio-alsa.c\ + lingot-audio-alsa.h\ + lingot-audio-jack.c\ + lingot-audio-jack.h\ + lingot-complex.h\ + lingot-complex.c\ + lingot-config.c\ + lingot-config.h\ + lingot-config-scale.c\ + lingot-config-scale.h\ + lingot-core.c\ + lingot-core.h\ + lingot-defs.h\ + lingot-msg.h\ + lingot-msg.c\ + lingot-gui-config-dialog.c\ + lingot-gui-config-dialog.h\ + lingot-gui-config-dialog-scale.c\ + lingot-gui-config-dialog-scale.h\ + lingot-gui-mainframe.c\ + lingot-gui-mainframe.h\ + lingot-gauge.c\ + lingot-gauge.h\ + lingot-filter.c\ + lingot-filter.h\ + lingot-signal.c\ + lingot-signal.h\ + lingot.c\ + lingot-i18n.h + +lingot_LDFLAGS = + +lingot_LDADD = \ + $(PACKAGE_LIBS) $(GTK_LIBS) $(ALSA_LIBS) $(JACK_LIBS) $(GLADE_LIBS)\ + -lpthread + diff --git a/src/glade/Makefile.am b/src/glade/Makefile.am new file mode 100644 index 0000000..079ffbc --- /dev/null +++ b/src/glade/Makefile.am @@ -0,0 +1,8 @@ +gladedir = $(datadir)/lingot/glade/ + +glade_DATA = \ + lingot-config-dialog.glade\ + lingot-mainframe.glade + + +EXTRA_DIST = $(glade_DATA) diff --git a/src/glade/lingot-config-dialog.glade b/src/glade/lingot-config-dialog.glade new file mode 100644 index 0000000..faf1c21 --- /dev/null +++ b/src/glade/lingot-config-dialog.glade @@ -0,0 +1,1085 @@ +<?xml version="1.0"?> +<glade-interface> + <!-- interface-requires gtk+ 2.12 --> + <!-- interface-naming-policy toplevel-contextual --> + <widget class="GtkDialog" id="dialog1"> + <property name="visible">True</property> + <property name="border_width">5</property> + <property name="title" translatable="yes">lingot configuration</property> + <property name="window_position">center-always</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox5"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <widget class="GtkNotebook" id="notebook1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <widget class="GtkVBox" id="vbox2"> + <property name="visible">True</property> + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="border_width">10</property> + <property name="n_rows">3</property> + <property name="n_columns">3</property> + <property name="column_spacing">10</property> + <property name="row_spacing">10</property> + <child> + <widget class="GtkLabel" id="system"> + <property name="visible">True</property> + <property name="label" translatable="yes">Audio system</property> + </widget> + <packing> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkComboBox" id="input_system"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Here you can pick the sound system.</property> + <property name="items">OSS +ALSA +JACK</property> + <signal name="changed" handler="on_combobox_input_architecture_changed"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="oss_label_input_dev2"> + <property name="visible">True</property> + <property name="label"> +</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="label" translatable="yes">Audio device</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkComboBoxEntry" id="input_dev"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Choose the audio device if you have more than one. You cannot modify this field if you are using JACK, but you can connect Lingot to a desired source externally.</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="label" translatable="yes">Sample rate</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label30"> + <property name="visible">True</property> + <property name="label" translatable="yes">Hz</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkComboBoxEntry" id="sample_rate"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Choose the hardware sample rate here. We give you some examples, but you can choose another one. Please, be aware that depending on the audio system and/or device, not all the sampling frequencies are available. Anyway, you can get close to your desired effective sample rate by combining this parameter with the oversampling factor (Basic tab). If you are using JACK, you cannot modify this field, but you can change it externally (in the JACK server configuration).</property> + <property name="items" translatable="yes"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_SHRINK | GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="position">0</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="input"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Audio input settings. Set here the source and hardware settings.</property> + <property name="label" translatable="yes">Capture</property> + </widget> + <packing> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table3"> + <property name="visible">True</property> + <property name="extension_events">cursor</property> + <property name="border_width">10</property> + <property name="n_rows">4</property> + <property name="n_columns">3</property> + <property name="column_spacing">10</property> + <property name="row_spacing">10</property> + <child> + <widget class="GtkLabel" id="label10"> + <property name="visible">True</property> + <property name="label" translatable="yes">Calculation rate</property> + </widget> + <packing> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label11"> + <property name="visible">True</property> + <property name="label" translatable="yes">Visualization rate</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label12"> + <property name="visible">True</property> + <property name="label" translatable="yes">Noise threshold</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label13"> + <property name="visible">True</property> + <property name="label" translatable="yes">Gain</property> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label14"> + <property name="visible">True</property> + <property name="label" translatable="yes">Hz</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label15"> + <property name="visible">True</property> + <property name="label" translatable="yes">Hz</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_EXPAND</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label16"> + <property name="visible">True</property> + <property name="label" translatable="yes">dB</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label17"> + <property name="visible">True</property> + <property name="label" translatable="yes">dB</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHScale" id="calculation_rate"> + <property name="width_request">100</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Number of calculations of the fundamental frequency per second.</property> + <property name="adjustment">25.199999999999999 1 30 1 0 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHScale" id="visualization_rate"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Number of visualizations per second of the (last) computed frequency, error and spectrum. </property> + <property name="adjustment">25 1 40 3 0 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHScale" id="noise_threshold"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Any signal level below this value will be considered as noise. This noise level is depicted in the spectrum area as a yellow dotted line.</property> + <property name="adjustment">90 0 100 1 10 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHScale" id="gain"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Allows you to adapt the signal levels without the aid of an external mixer.</property> + <property name="adjustment">27.699999999999999 -90 90 0.10000000000000001 0 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Quick adjusments on the dynamic response and the signal levels.</property> + <property name="label" translatable="yes">Adjustments</property> + </widget> + <packing> + <property name="position">1</property> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table4"> + <property name="visible">True</property> + <property name="border_width">10</property> + <property name="n_rows">4</property> + <property name="n_columns">3</property> + <property name="column_spacing">10</property> + <property name="row_spacing">10</property> + <child> + <widget class="GtkSpinButton" id="temporal_window"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">This is the most recent amount of data considered for tuning. The longer it is, the more accuracy you can obtain, but also the dynamic response gets lower, because you are considering older parts of the signal. Also, if you raise this parameter, the computational cost increases. This value, in samples (depending on the effective sample rate), must be greater or equal to the FFT buffer size.</property> + <property name="xalign">1</property> + <property name="adjustment">0.32000000000000001 0 15 0.01 0 0</property> + <property name="digits">3</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkComboBox" id="fft_size"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">The FFT buffer gives Lingot a first look to the spectrum. Higher values can help Lingot to hook up the correct peak, but it's also computationally more expensive. Don't use high values here unless you have an equally high effective sample rate.</property> + <property name="items">256 +512 +1024 +2048 +4096</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table7"> + <property name="visible">True</property> + <property name="n_columns">4</property> + <child> + <widget class="GtkLabel" id="label_sample_rate1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Hardware sampling frequency</property> + <property name="label">8000</property> + </widget> + <packing> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label31"> + <property name="visible">True</property> + <property name="label"> / </property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="oversampling"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_tooltip">True</property> + <property name="tooltip" translatable="yes">Oversampling factor</property> + <property name="xalign">1</property> + <property name="adjustment">1 1 120 1 10 0</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_sample_rate2"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Effective sampling frequency. The lower this parameter is, the more accurate the FFT will be, and hence the better is the hooking up to the correct peak. Remember, however, that your temporal window will increase, and the fact that you won't be able to tune frequencies above the half of this frequency (Nyquist-Shannon sampling theorem).</property> + <property name="label"> = 2000</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label28"> + <property name="visible">True</property> + <property name="label" translatable="yes">seconds</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label27"> + <property name="visible">True</property> + <property name="label" translatable="yes">samples</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label24"> + <property name="visible">True</property> + <property name="label" translatable="yes">Hz</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label22"> + <property name="visible">True</property> + <property name="label" translatable="yes">Temporal window</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label21"> + <property name="visible">True</property> + <property name="label" translatable="yes">FFT size</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label20"> + <property name="visible">True</property> + <property name="label" translatable="yes">Effective sample rate</property> + </widget> + <packing> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label23"> + <property name="visible">True</property> + <property name="label" translatable="yes">Minimum frequency</property> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="minimum_frequency"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Lingot only considers frequencies above this one.</property> + <property name="invisible_char">●</property> + <property name="xalign">1</property> + <property name="adjustment">0 0 22050 1 0 0</property> + <property name="digits">1</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label29"> + <property name="visible">True</property> + <property name="label" translatable="yes">Hz</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Basic settings on the frequency finding algorithm. Come here to get better accuracy, frequency range or CPU usage. </property> + <property name="label" translatable="yes">Basic</property> + </widget> + <packing> + <property name="position">2</property> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table5"> + <property name="visible">True</property> + <property name="border_width">10</property> + <property name="n_rows">5</property> + <property name="n_columns">3</property> + <property name="column_spacing">10</property> + <property name="row_spacing">10</property> + <child> + <widget class="GtkLabel" id="label48"> + <property name="visible">True</property> + <property name="label" translatable="yes">dB</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label47"> + <property name="visible">True</property> + <property name="label" translatable="yes">samples</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label45"> + <property name="visible">True</property> + <property name="label" translatable="yes">peaks</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label46"> + <property name="visible">True</property> + <property name="label" translatable="yes">samples</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label44"> + <property name="visible">True</property> + <property name="label" translatable="yes">DFTs</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHScale" id="rejection_peak_relation"> + <property name="width_request">100</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">All the peaks with an amplitude difference with the highest one greater than this parameter will be rejected as a candidate to host the fundamental frequency</property> + <property name="adjustment">29.199999999999999 0 50 1 0 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="peak_halfwidth"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Number of samples needed by a peak at each side, lower in amplitude than the sample at the center, to be considered as a peak. You should not consider more than 1 or 2 samples, unless you have a really big FFT buffer.</property> + <property name="xalign">1</property> + <property name="adjustment">1 1 5 1 10 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="peak_number"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Number of main peaks considered over the FFT. Remember that sometimes the first harmonics can have a higher amplitude than the fundamental component, depending on your instrument.</property> + <property name="xalign">1</property> + <property name="adjustment">1 1 10 1 10 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="dft_size"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Number of samples of each DFT.</property> + <property name="xalign">1</property> + <property name="adjustment">4 4 100 1 10 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label43"> + <property name="visible">True</property> + <property name="label" translatable="yes">Peak rejection relation</property> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="dft_number"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">After having chosen the fundamental peak on the FFT, Lingot computes a series of N (being N this parameter) DFTs localized in frequency to enclose the fundamental frequency.</property> + <property name="xalign">1</property> + <property name="adjustment">0 0 10 1 10 0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label33"> + <property name="visible">True</property> + <property name="label" translatable="yes">Peak half-width</property> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label32"> + <property name="visible">True</property> + <property name="label" translatable="yes">Peak number</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label25"> + <property name="visible">True</property> + <property name="label" translatable="yes">DFT size</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label18"> + <property name="visible">True</property> + <property name="label" translatable="yes">DFT number</property> + </widget> + <packing> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="position">3</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Advanced settings on the frequency finding algorithm. You should not modify these parameters unless you really need it and you have a good understanding of the algorithm. Use the 'Default' button if you get lost, and use those default parameters as your reference.</property> + <property name="label" translatable="yes">Advanced</property> + </widget> + <packing> + <property name="position">3</property> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <child> + <widget class="GtkLabel" id="label8"> + <property name="visible">True</property> + <property name="xalign">0.47999998927116394</property> + <property name="label" translatable="yes">Name</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">21</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="scale_name"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Scale name, only for your information.</property> + <property name="invisible_char">●</property> + </widget> + <packing> + <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox2"> + <property name="visible">True</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="height_request">200</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">never</property> + <property name="vscrollbar_policy">automatic</property> + <property name="shadow_type">out</property> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">4</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="vbox3"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <widget class="GtkVButtonBox" id="vbuttonbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <widget class="GtkButton" id="button_scale_add"> + <property name="label" translatable="yes">Insert</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip" translatable="yes">Adds a new note to the list, just above the selected line, or appends it to the end if no line is selected. Is not possible to add a note before the reference (the first) one. You can also use the Insert key on the table beside.</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_scale_del"> + <property name="label" translatable="yes">Delete</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip" translatable="yes">Deletes the selected notes. The reference note, i.e., the first one, cannot be removed. You can also use the Delete key on the table beside.</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_scale_import"> + <property name="label" translatable="yes">Import</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip" translatable="yes">Imports the scale from an external .scl file, with the Scala project format (http://www.huygens-fokker.org/scala/)</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox3"> + <property name="visible">True</property> + <child> + <widget class="GtkHBox" id="hbox4"> + <property name="visible">True</property> + <child> + <widget class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="label" translatable="yes">Deviation</property> + </widget> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="root_frequency_error"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Applies a shift in frequency to all the notes defined in the table above.</property> + <property name="invisible_char">●</property> + <property name="xalign">1</property> + <property name="adjustment">0 -500 500 1 10 0</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label19"> + <property name="visible">True</property> + <property name="ypad">1</property> + <property name="label" translatable="yes">cents</property> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label26"> + <property name="visible">True</property> + </widget> + <packing> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="position">4</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Lingot is an universal tuner, it can tune any instrument according to the scale you define here.</property> + <property name="xpad">1</property> + <property name="label" translatable="yes">Scale</property> + </widget> + <packing> + <property name="position">4</property> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area5"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <widget class="GtkButton" id="button_default"> + <property name="label" translatable="yes">Default</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip" translatable="yes">Resets the built-in settings, without applying them.</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_apply"> + <property name="label">gtk-apply</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip" translatable="yes">Apply the current changes to the running tuner, but doesn't modify the configuration file.</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_accept"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip" translatable="yes">Apply the current changes and modify the congifuration file.</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip" translatable="yes">Rolls back all the previous applied changes and closes the configuration dialog.</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/src/glade/lingot-mainframe.glade b/src/glade/lingot-mainframe.glade new file mode 100644 index 0000000..ce76be9 --- /dev/null +++ b/src/glade/lingot-mainframe.glade @@ -0,0 +1,273 @@ +<?xml version="1.0"?> +<glade-interface> + <!-- interface-requires gtk+ 2.16 --> + <!-- interface-naming-policy project-wide --> + <widget class="GtkWindow" id="window1"> + <property name="visible">True</property> + <property name="no_show_all">True</property> + <property name="border_width">6</property> + <property name="title" translatable="yes">lingot</property> + <property name="resizable">False</property> + <property name="window_position">center</property> + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <widget class="GtkMenuBar" id="menubar1"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="menuitem1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu1"> + <property name="visible">True</property> + <child> + <widget class="GtkImageMenuItem" id="open_config_item"> + <property name="label">gtk-open</property> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Opens an external configuration file.</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="save_config_item"> + <property name="label">gtk-save-as</property> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Saves the running configuration to an external file.</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separatormenuitem1"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="quit_item"> + <property name="label">gtk-quit</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menuitem2"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu2"> + <property name="visible">True</property> + <child> + <widget class="GtkImageMenuItem" id="preferences_item"> + <property name="label">gtk-preferences</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menuitem3"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu3"> + <property name="visible">True</property> + <child> + <widget class="GtkCheckMenuItem" id="spectrum_item"> + <property name="visible">True</property> + <property name="label" translatable="yes">Show spectrum</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menuitem4"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu4"> + <property name="visible">True</property> + <child> + <widget class="GtkImageMenuItem" id="about_item"> + <property name="label">gtk-about</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="spacing">4</property> + <child> + <widget class="GtkFrame" id="frame1"> + <property name="width_request">164</property> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">in</property> + <child> + <widget class="GtkDrawingArea" id="gauge_area"> + <property name="width_request">160</property> + <property name="height_request">100</property> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Shows the error in cents in a visual way. The range will depend on the maximum distance between each two notes in the scale defined in the Lingot settings (try to provide scales with low maximum distance, i.e. with many notes, to have a higher resolution in this gauge),</property> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Deviation</property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkFrame" id="frame3"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">in</property> + <child> + <widget class="GtkAlignment" id="alignment3"> + <property name="visible">True</property> + <property name="left_padding">12</property> + <child> + <widget class="GtkVBox" id="vbox2"> + <property name="width_request">110</property> + <property name="height_request">100</property> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <widget class="GtkLabel" id="freq_label"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Estimated fundamental frequency in hertzs.</property> + <property name="label" translatable="yes">f = ---</property> + </widget> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="tone_label"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Closest note to the estimated frequency, according to the scale defined in the Lingot settings.</property> + <property name="label" translatable="yes">---</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="error_label"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Error in cents between the estimated frequency and the closest note according to the scale defined in the Lingot settings.</property> + <property name="label" translatable="yes">e = ---</property> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="label" translatable="yes">Tone</property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkFrame" id="spectrum_frame"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">in</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="width_request">290</property> + <property name="height_request">96</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <widget class="GtkViewport" id="viewport1"> + <property name="visible">True</property> + <property name="resize_mode">queue</property> + <child> + <widget class="GtkDrawingArea" id="spectrum_area"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">This area shows the spectral power density of the captured signal. The fundamental frequency is shown with a red dot, and the noise threshold with a horizontal dotted yellow line. If you have a large FFT buffer (with more than 512 samples), you can browse through the whole spectrum with a horizontal scrollbar.</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes">Spectrum</property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/src/lingot-audio-alsa.c b/src/lingot-audio-alsa.c new file mode 100644 index 0000000..2c134f3 --- /dev/null +++ b/src/lingot-audio-alsa.c @@ -0,0 +1,205 @@ +/* + * 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 <stdlib.h> + +#include "lingot-defs.h" +#include "lingot-audio-alsa.h" +#include "lingot-i18n.h" +#include "lingot-msg.h" + +LingotAudioHandler* lingot_audio_alsa_new(char* device, int sample_rate) { + + LingotAudioHandler* audio = NULL; + +# ifdef ALSA + const char* exception; + snd_pcm_hw_params_t* hw_params = NULL; + int err; + char error_message[1000]; + unsigned int channels = 1; + + audio = malloc(sizeof(LingotAudioHandler)); + audio->read_buffer = NULL; + audio->audio_system = AUDIO_SYSTEM_ALSA; + audio->read_buffer_size = 128; // TODO: size up + + // ALSA allocates some mem to load its config file when we call + // snd_card_next. Now that we're done getting the info, let's tell ALSA + // to unload the info and free up that mem + snd_config_update_free_global(); + + audio->capture_handle = NULL; + + try { + if ((err = snd_pcm_open(&audio->capture_handle, device, + SND_PCM_STREAM_CAPTURE, 0)) < 0) { + sprintf(error_message, "Cannot open audio device %s.\n%s.", device, + snd_strerror(err)); + throw(error_message); + } + + strcpy(audio->device, device); + + if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { + sprintf(error_message, + "Cannot allocate hardware parameter structure.\n%s.", + snd_strerror(err)); + throw(error_message); + } + + if ((err = snd_pcm_hw_params_any(audio->capture_handle, hw_params)) < 0) { + sprintf(error_message, + "Cannot initialize hardware parameter structure.\n%s.", + snd_strerror(err)); + throw(error_message); + } + + if ((err = snd_pcm_hw_params_set_access(audio->capture_handle, + hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + sprintf(error_message, "Cannot set access type.\n%s", snd_strerror( + err)); + throw(error_message); + } + + if ((err = snd_pcm_hw_params_set_format(audio->capture_handle, + hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + sprintf(error_message, "Cannot set sample format.\n%s.", + snd_strerror(err)); + throw(error_message); + } + + unsigned int rate = sample_rate; + + if ((err = snd_pcm_hw_params_set_rate_near(audio->capture_handle, + hw_params, &rate, 0)) < 0) { + sprintf(error_message, "Cannot set sample rate.\n%s.", + snd_strerror(err)); + throw(error_message); + } + + audio->real_sample_rate = rate; + + if ((err = snd_pcm_hw_params_set_channels(audio->capture_handle, + hw_params, channels)) < 0) { + sprintf(error_message, "Cannot set channel count.\n%s.", + snd_strerror(err)); + throw(error_message); + } + + if ((err = snd_pcm_hw_params(audio->capture_handle, hw_params)) < 0) { + sprintf(error_message, "Cannot set parameters.\n%s.", snd_strerror( + err)); + throw(error_message); + } + + if ((err = snd_pcm_prepare(audio->capture_handle)) < 0) { + sprintf(error_message, + "Cannot prepare audio interface for use.\n%s.", + snd_strerror(err)); + throw(error_message); + } + + audio->read_buffer = malloc(channels * audio->read_buffer_size + * sizeof(SAMPLE_TYPE)); + memset(audio->read_buffer, 0, audio->read_buffer_size + * sizeof(SAMPLE_TYPE)); + } catch { + if (audio->capture_handle != NULL) + snd_pcm_close(audio->capture_handle); + free(audio); + audio = NULL; + lingot_msg_add_error(exception); + } + + if (hw_params != NULL) + snd_pcm_hw_params_free(hw_params); + +# else + lingot_msg_add_error( + _("The application has not been built with ALSA support")); +# endif + + return audio; +} + +void lingot_audio_alsa_destroy(LingotAudioHandler* audio) { +# ifdef ALSA + if (audio != NULL) { + snd_pcm_close(audio->capture_handle); + free(audio->read_buffer); + } +# endif +} + +int lingot_audio_alsa_read(LingotAudioHandler* audio) { +# ifdef ALSA + int temp_sret; + int i; + + temp_sret = snd_pcm_readi(audio->capture_handle, audio->read_buffer, + audio->read_buffer_size); + + // if (rand() < 0.001 * RAND_MAX) + // temp_sret = 0; + + if (temp_sret != audio->read_buffer_size) { + char buff[100]; + sprintf(buff, _("Read from audio interface failed.\n%s."), + snd_strerror(temp_sret)); + lingot_msg_add_error(buff); + return -1; + } + + // float point conversion + for (i = 0; i < audio->read_buffer_size; i++) { + audio->flt_read_buffer[i] = audio->read_buffer[i]; + } + +# endif + + return 0; +} + +LingotAudioSystemProperties* lingot_audio_alsa_get_audio_system_properties( + audio_system_t audio_system) { + + LingotAudioSystemProperties* result = + (LingotAudioSystemProperties*) malloc(1 + * sizeof(LingotAudioSystemProperties)); + + // TODO + result->forced_sample_rate = 0; + result->n_devices = 0; + result->devices = NULL; + + result->n_sample_rates = 5; + result->sample_rates = malloc(result->n_sample_rates * sizeof(int)); + result->sample_rates[0] = 8000; + result->sample_rates[1] = 11025; + result->sample_rates[2] = 22050; + result->sample_rates[3] = 44100; + result->sample_rates[4] = 48000; + + return result; +} + diff --git a/src/lingot-audio-alsa.h b/src/lingot-audio-alsa.h new file mode 100644 index 0000000..3580c1e --- /dev/null +++ b/src/lingot-audio-alsa.h @@ -0,0 +1,34 @@ +/* + * 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 + */ + +#ifndef __LINGOT_AUDIO_ALSA_H__ +#define __LINGOT_AUDIO_ALSA_H__ + +#include "lingot-audio.h" + +LingotAudioHandler* lingot_audio_alsa_new(char* device, int sample_rate); +void lingot_audio_alsa_destroy(LingotAudioHandler*); +int lingot_audio_alsa_read(LingotAudioHandler*); +LingotAudioSystemProperties* lingot_audio_alsa_get_audio_system_properties( + audio_system_t); + +#endif diff --git a/src/lingot-audio-jack.c b/src/lingot-audio-jack.c new file mode 100644 index 0000000..5135c38 --- /dev/null +++ b/src/lingot-audio-jack.c @@ -0,0 +1,349 @@ +/* + * 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 <stdio.h> + +#include "lingot-defs.h" +#include "lingot-audio-jack.h" +#include "lingot-i18n.h" +#include "lingot-msg.h" + +#ifdef JACK +#include <jack/jack.h> + +// persistent JACK client to obtain hardware parameters +jack_client_t* client = NULL; +pthread_mutex_t stop_mutex = PTHREAD_MUTEX_INITIALIZER; + +// this array allows us to reconnect the client to the last ports it was +// connected in a previous session +#define MAX_LAST_PORTS 10 +char last_ports[MAX_LAST_PORTS][80]; + +int lingot_audio_jack_process(jack_nframes_t nframes, void* param) { + LingotAudioHandler* audio = param; + audio->nframes = nframes; + + pthread_mutex_lock(&stop_mutex); + if (audio->running) { + lingot_audio_jack_read(audio); + audio->process_callback(audio->flt_read_buffer, + audio->read_buffer_size, audio->process_callback_arg); + } + pthread_mutex_unlock(&stop_mutex); + + return 0; +} + +// JACK calls this shutdown_callback if the server ever shuts down or +// decides to disconnect the client. +void lingot_audio_jack_shutdown(void* param) { + LingotAudioHandler* audio = param; + lingot_msg_add_error(_("Missing connection with JACK audio server")); + pthread_mutex_lock(&stop_mutex); + audio->interrupted = 1; + pthread_mutex_unlock(&stop_mutex); +} +#endif + +LingotAudioHandler* lingot_audio_jack_new(char* device, int sample_rate) { + + LingotAudioHandler* audio = NULL; + +# ifdef JACK + const char* exception; + const char **ports = NULL; + const char *client_name = "lingot"; + const char *server_name = NULL; + + jack_options_t options = JackNoStartServer; + jack_status_t status; + + audio = malloc(sizeof(LingotAudioHandler)); + strcpy(audio->device, ""); + + audio->audio_system = AUDIO_SYSTEM_JACK; + audio->jack_client = jack_client_open(client_name, options, &status, + server_name); + + try { + if (audio->jack_client == NULL) { + throw(_("Unable to connect to JACK server")); + } + + if (status & JackServerStarted) { + fprintf(stderr, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + client_name = jack_get_client_name(audio->jack_client); + fprintf(stderr, "unique name `%s' assigned\n", client_name); + } + + jack_on_shutdown(audio->jack_client, lingot_audio_jack_shutdown, audio); + + audio->real_sample_rate = jack_get_sample_rate(audio->jack_client); + audio->read_buffer_size = jack_get_buffer_size(audio->jack_client); + + // printf("engine sample rate: %" PRIu32 "\n", jack_get_sample_rate( + // audio->jack_client)); + // printf("buffer size: %" PRIu32 "\n", jack_get_buffer_size( + // audio->jack_client)); + + audio->jack_input_port = jack_port_register(audio->jack_client, + "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + + if ((audio->jack_input_port == NULL)) { + throw(_("No more JACK ports available")); + } + + } catch { + free(audio); + audio = NULL; + lingot_msg_add_error(exception); + } + + if (ports != NULL) + free(ports); + + if (audio != NULL) { + client = audio->jack_client; + } + +# else + lingot_msg_add_error( + _("The application has not been built with JACK support")); +# endif + return audio; +} + +void lingot_audio_jack_destroy(LingotAudioHandler* audio) { +# ifdef JACK + if (audio != NULL) { + //jack_cycle_wait(audio->jack_client); + // jack_deactivate(audio->jack_client); + jack_client_close(audio->jack_client); + client = NULL; + } +# endif +} + +int lingot_audio_jack_read(LingotAudioHandler* audio) { +# ifdef JACK + register int i; + float* in = jack_port_get_buffer(audio->jack_input_port, audio->nframes); + for (i = 0; i < audio->nframes; i++) + audio->flt_read_buffer[i] = in[i] * 32768; + return 0; +# else + return -1; +# endif +} + +LingotAudioSystemProperties* lingot_audio_jack_get_audio_system_properties( + audio_system_t audio_system) { + + LingotAudioSystemProperties* properties = NULL; +# ifdef JACK + properties = (LingotAudioSystemProperties*) malloc(1 + * sizeof(LingotAudioSystemProperties)); + + int sample_rate = -1; + + const char *client_name = "lingot-get-sample-rate"; + const char *server_name = NULL; + + jack_options_t options = JackNoStartServer; + jack_status_t status; + jack_client_t* jack_client = NULL; + const char **ports = NULL; + const char* exception; + + unsigned long int flags = JackPortIsOutput; + + try { + if (client != NULL) { + sample_rate = jack_get_sample_rate(client); + ports = jack_get_ports(client, NULL, NULL, flags); + } else { + jack_client = jack_client_open(client_name, options, &status, + server_name); + if (jack_client == NULL) { + throw(_("Unable to connect to JACK server")); + } + if (status & JackServerStarted) { + fprintf(stderr, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + client_name = jack_get_client_name(jack_client); + fprintf(stderr, "unique name `%s' assigned\n", client_name); + } + + sample_rate = jack_get_sample_rate(jack_client); + + ports = jack_get_ports(jack_client, NULL, NULL, flags); + } + } catch { + // here I throw a warning message because we are only ontaining the + // audio properties + lingot_msg_add_warning(exception); + } + + properties->forced_sample_rate = 1; + properties->n_devices = 0; + properties->devices = NULL; + + if (ports != NULL) { + int i; + for (i = 0; ports[i] != NULL; i++) { + } + properties->n_devices = i; + + if (properties->n_devices != 0) { + properties->devices = malloc(properties->n_devices * sizeof(char*)); + for (i = 0; ports[i] != NULL; i++) { + properties->devices[i] = strdup(ports[i]); + } + } + + } + + if (sample_rate == -1) { + properties->n_sample_rates = 0; + properties->sample_rates = NULL; + } else { + properties->n_sample_rates = 1; + properties->sample_rates = malloc(properties->n_sample_rates + * sizeof(int)); + properties->sample_rates[0] = sample_rate; + } + + if (ports != NULL) + free(ports); + + if (jack_client != NULL) + jack_client_close(jack_client); + +# else + lingot_msg_add_error( + _("The application has not been built with JACK support")); +# endif + + return properties; +} + +int lingot_audio_jack_start(LingotAudioHandler* audio) { + int result = 0; + +# ifdef JACK + int index = 0; + const char **ports = NULL; + const char* exception; + jack_set_process_callback(audio->jack_client, lingot_audio_jack_process, + audio); + + try { + if (jack_activate(audio->jack_client)) { + throw(_("Cannot activate client")); + } + + ports + = jack_get_ports(audio->jack_client, NULL, NULL, + JackPortIsOutput); + if (ports == NULL) { + throw(_("No active capture ports")); + } + + // try to connect the client to the ports is was connected before + int j = 0; + int connections = 0; + for (j = 0; j < MAX_LAST_PORTS; j++) { + for (index = 0; ports[index]; index++) { + if (!strcmp(last_ports[j], ports[index])) { + if (jack_connect(audio->jack_client, ports[index], + jack_port_name(audio->jack_input_port))) { + throw(_("Cannot connect input ports")); + } else { + connections++; + } + } + } + } + + // if there wasn't connections before, we connect the client to the + // first physical port + if (!connections) { + free(ports); + ports = jack_get_ports(audio->jack_client, NULL, NULL, + JackPortIsPhysical | JackPortIsOutput); + if (ports == NULL) { + throw(_("No physical capture ports")); + } + + if (jack_connect(audio->jack_client, ports[0], jack_port_name( + audio->jack_input_port))) { + throw(_("Cannot connect input ports")); + } + } + + } catch { + lingot_msg_add_error(exception); + result = -1; + } + + free(ports); +# else + lingot_msg_add_error( + _("The application has not been built with JACK support")); +# endif + + return result; +} + +void lingot_audio_jack_stop(LingotAudioHandler* audio) { +# ifdef JACK + //jack_cycle_wait(audio->jack_client); + const char** ports = jack_get_ports(audio->jack_client, NULL, NULL, + JackPortIsOutput); + + if (ports != NULL) { + int i, j = 0; + + for (i = 0; i < MAX_LAST_PORTS; i++) { + strcpy(last_ports[i], ""); + } + + for (i = 0; ports[i]; i++) { + if (jack_port_connected(jack_port_by_name(audio->jack_client, + ports[i]))) { + strcpy(last_ports[j++], ports[i]); + } + } + } + + pthread_mutex_lock(&stop_mutex); + jack_deactivate(audio->jack_client); + pthread_mutex_unlock(&stop_mutex); +# else + lingot_msg_add_error( + _("The application has not been built with JACK support")); +# endif +} diff --git a/src/lingot-audio-jack.h b/src/lingot-audio-jack.h new file mode 100644 index 0000000..92495cc --- /dev/null +++ b/src/lingot-audio-jack.h @@ -0,0 +1,36 @@ +/* + * 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 + */ + +#ifndef __LINGOT_AUDIO_JACK_H__ +#define __LINGOT_AUDIO_JACK_H__ + +#include "lingot-audio.h" + +LingotAudioHandler* lingot_audio_jack_new(char* device, int sample_rate); +void lingot_audio_jack_destroy(LingotAudioHandler*); +int lingot_audio_jack_read(LingotAudioHandler*); +LingotAudioSystemProperties* lingot_audio_jack_get_audio_system_properties( + audio_system_t); +int lingot_audio_jack_start(LingotAudioHandler*); +void lingot_audio_jack_stop(LingotAudioHandler*); + +#endif diff --git a/src/lingot-audio-oss.c b/src/lingot-audio-oss.c new file mode 100644 index 0000000..ee43ae6 --- /dev/null +++ b/src/lingot-audio-oss.c @@ -0,0 +1,168 @@ +/* + * 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 <sys/soundcard.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "lingot-msg.h" +#include "lingot-defs.h" +#include "lingot-audio-oss.h" +#include "lingot-i18n.h" + +LingotAudioHandler* lingot_audio_oss_new(char* device, int sample_rate) { + + int channels = 1; + int format = SAMPLE_FORMAT; + char error_message[100]; + const char* exception; + + LingotAudioHandler* audio = malloc(sizeof(LingotAudioHandler)); + + audio->audio_system = AUDIO_SYSTEM_OSS; + audio->dsp = open(device, O_RDONLY); + audio->read_buffer_size = 128; // TODO: size up + strcpy(audio->device, device); + + try { + + if (audio->dsp < 0) { + sprintf(error_message, _("Unable to open audio device %s.\n%s."), + device, strerror(errno)); + throw(error_message); + } + + //if (ioctl(audio->dsp, SOUND_PCM_READ_CHANNELS, &channels) < 0) + if (ioctl(audio->dsp, SNDCTL_DSP_CHANNELS, &channels) < 0) { + sprintf(error_message, _("Error setting number of channels.\n%s."), + strerror(errno)); + throw(error_message); + } + + // sample size + //if (ioctl(audio->dsp, SOUND_PCM_SETFMT, &format) < 0) + if (ioctl(audio->dsp, SNDCTL_DSP_SETFMT, &format) < 0) { + sprintf(error_message, _("Error setting bits per sample.\n%s."), + strerror(errno)); + throw(error_message); + } + + int fragment_size = 1; + int DMA_buffer_size = 512; + int param = 0; + + for (param = 0; fragment_size < DMA_buffer_size; param++) + fragment_size <<= 1; + + param |= 0x00ff0000; + + if (ioctl(audio->dsp, SNDCTL_DSP_SETFRAGMENT, ¶m) < 0) { + sprintf(error_message, _("Error setting DMA buffer size.\n%s."), + strerror(errno)); + throw(error_message); + } + + if (ioctl(audio->dsp, SNDCTL_DSP_SPEED, &sample_rate) < 0) { + sprintf(error_message, _("Error setting sample rate.\n%s."), + strerror(errno)); + throw(error_message); + } + + audio->real_sample_rate = sample_rate; + audio->read_buffer = malloc(audio->read_buffer_size + * sizeof(SAMPLE_TYPE)); + memset(audio->read_buffer, 0, audio->read_buffer_size + * sizeof(SAMPLE_TYPE)); + + } catch { + close(audio->dsp); + free(audio); + audio = NULL; + lingot_msg_add_error(exception); + } + + return audio; +} + +void lingot_audio_oss_destroy(LingotAudioHandler* audio) { + if (audio != NULL) { + close(audio->dsp); + free(audio->read_buffer); + } +} + +int lingot_audio_oss_read(LingotAudioHandler* audio) { + int i; + int read_size; + + read_size = read(audio->dsp, audio->read_buffer, audio->read_buffer_size + * sizeof(SAMPLE_TYPE)); + + // if (rand() < 0.001 * RAND_MAX) + // read_size = 0; + + if (read_size != audio->read_buffer_size * sizeof(SAMPLE_TYPE)) { + char buff[100]; + sprintf(buff, _("Read from audio interface failed.\n%s."), strerror( + errno)); + lingot_msg_add_error(buff); + return -1; + } + + // float point conversion + for (i = 0; i < audio->read_buffer_size; i++) { + audio->flt_read_buffer[i] = audio->read_buffer[i]; + } + + return 0; +} + +LingotAudioSystemProperties* lingot_audio_oss_get_audio_system_properties( + audio_system_t audio_system) { + + LingotAudioSystemProperties* result = + (LingotAudioSystemProperties*) malloc(1 + * sizeof(LingotAudioSystemProperties)); + + // TODO + result->forced_sample_rate = 0; + result->n_devices = 0; + result->devices = NULL; + + result->n_sample_rates = 5; + result->sample_rates = malloc(result->n_sample_rates * sizeof(int)); + result->sample_rates[0] = 8000; + result->sample_rates[1] = 11025; + result->sample_rates[2] = 22050; + result->sample_rates[3] = 44100; + result->sample_rates[4] = 48000; + + return result; +} + diff --git a/src/lingot-audio-oss.h b/src/lingot-audio-oss.h new file mode 100644 index 0000000..a87e204 --- /dev/null +++ b/src/lingot-audio-oss.h @@ -0,0 +1,34 @@ +/* + * 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 + */ + +#ifndef __LINGOT_AUDIO_OSS_H__ +#define __LINGOT_AUDIO_OSS_H__ + +#include "lingot-audio.h" + +LingotAudioHandler* lingot_audio_oss_new(char* device, int sample_rate); +void lingot_audio_oss_destroy(LingotAudioHandler*); +int lingot_audio_oss_read(LingotAudioHandler*); +LingotAudioSystemProperties* lingot_audio_oss_get_audio_system_properties( + audio_system_t); + +#endif diff --git a/src/lingot-audio.c b/src/lingot-audio.c new file mode 100644 index 0000000..7e81ab1 --- /dev/null +++ b/src/lingot-audio.c @@ -0,0 +1,208 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lingot-defs.h" +#include "lingot-audio.h" + +#include "lingot-core.h" +#include "lingot-audio-oss.h" +#include "lingot-audio-alsa.h" +#include "lingot-audio-jack.h" + +LingotAudioHandler* lingot_audio_new(audio_system_t audio_system, char* device, + int sample_rate, LingotAudioProcessCallback process_callback, + void *process_callback_arg) { + + LingotAudioHandler* result = NULL; + + switch (audio_system) { + case AUDIO_SYSTEM_OSS: + result = lingot_audio_oss_new(device, sample_rate); + break; + case AUDIO_SYSTEM_ALSA: + result = lingot_audio_alsa_new(device, sample_rate); + break; + case AUDIO_SYSTEM_JACK: + result = lingot_audio_jack_new(device, sample_rate); + break; + } + + if (result != NULL) { + // audio source read in floating point format. + result->flt_read_buffer + = malloc(result->read_buffer_size * sizeof(FLT)); + memset(result->flt_read_buffer, 0, result->read_buffer_size + * sizeof(FLT)); + result->process_callback = process_callback; + result->process_callback_arg = process_callback_arg; + result->interrupted = 0; + result->running = 0; + } + + return result; +} + +void lingot_audio_destroy(LingotAudioHandler* audio) { + if (audio != NULL) { + + free(audio->flt_read_buffer); + + switch (audio->audio_system) { + case AUDIO_SYSTEM_OSS: + lingot_audio_oss_destroy(audio); + break; + case AUDIO_SYSTEM_ALSA: + lingot_audio_alsa_destroy(audio); + break; + case AUDIO_SYSTEM_JACK: + lingot_audio_jack_destroy(audio); + break; + default: + perror("unknown audio system\n"); + break; + } + + free(audio); + } +} + +int lingot_audio_read(LingotAudioHandler* audio) { + int result = -1; + + if (audio != NULL) + switch (audio->audio_system) { + case AUDIO_SYSTEM_OSS: + result = lingot_audio_oss_read(audio); + break; + case AUDIO_SYSTEM_ALSA: + result = lingot_audio_alsa_read(audio); + break; + // case AUDIO_SYSTEM_JACK: + // result = lingot_audio_jack_read(audio); + // break; + default: + perror("unknown audio system\n"); + result = -1; + break; + } + + return result; +} + +LingotAudioSystemProperties* lingot_audio_get_audio_system_properties( + audio_system_t audio_system) { + LingotAudioSystemProperties* result; + + switch (audio_system) { + case AUDIO_SYSTEM_OSS: + result = lingot_audio_oss_get_audio_system_properties(audio_system); + break; + case AUDIO_SYSTEM_ALSA: + result = lingot_audio_alsa_get_audio_system_properties(audio_system); + break; + case AUDIO_SYSTEM_JACK: + result = lingot_audio_jack_get_audio_system_properties(audio_system); + break; + default: + perror("unknown audio system\n"); + result = NULL; + break; + } + + return result; +} + +void lingot_audio_audio_system_properties_destroy( + LingotAudioSystemProperties* properties) { + + int i; + for (i = 0; i < properties->n_devices; i++) { + free(properties->devices[i]); + } + + if (properties->sample_rates != NULL) + free(properties->sample_rates); + if (properties->devices != NULL) + free(properties->devices); +} + +void lingot_audio_run_reading_thread(LingotAudioHandler* audio) { + + int read_status = 0; + + while (audio->running) { + // process new data block. + read_status = lingot_audio_read(audio); + + if (read_status == 0) { + audio->process_callback(audio->flt_read_buffer, + audio->read_buffer_size, audio->process_callback_arg); + } else { + audio->running = 0; + audio->interrupted = 1; + } + } +} + +int lingot_audio_start(LingotAudioHandler* audio) { + + int result = 0; + + if (audio->audio_system != AUDIO_SYSTEM_JACK) { + pthread_attr_init(&audio->thread_input_read_attr); + + // detached thread. + // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&audio->thread_input_read, + &audio->thread_input_read_attr, + (void* (*)(void*)) lingot_audio_run_reading_thread, audio); + } else { + result = lingot_audio_jack_start(audio); + } + + if (result == 0) { + audio->running = 1; + } + + return result; +} + +void lingot_audio_stop(LingotAudioHandler* audio) { + void* thread_result; + + if (audio->running == 1) { + audio->running = 0; + // thread cancellation + if (audio->audio_system != AUDIO_SYSTEM_JACK) { + pthread_cancel(audio->thread_input_read); + pthread_join(audio->thread_input_read, &thread_result); + pthread_attr_destroy(&audio->thread_input_read_attr); + } else { + lingot_audio_jack_stop(audio); + } + } +} + diff --git a/src/lingot-audio.h b/src/lingot-audio.h new file mode 100644 index 0000000..d76995c --- /dev/null +++ b/src/lingot-audio.h @@ -0,0 +1,109 @@ +/* + * 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 + */ + +#ifndef __LINGOT_AUDIO_H__ +#define __LINGOT_AUDIO_H__ + +#ifdef ALSA +#include <alsa/asoundlib.h> +#endif + +#ifdef JACK +#include <jack/jack.h> +#endif + +#include "lingot-config.h" + +typedef void (*LingotAudioProcessCallback)(FLT* read_buffer, + int read_buffer_size, void *arg); + +typedef struct _LingotAudioHandler LingotAudioHandler; + +struct _LingotAudioHandler { + + int audio_system; + char device[100]; + + LingotAudioProcessCallback process_callback; + void* process_callback_arg; + +#ifdef ALSA + snd_pcm_t *capture_handle; +#endif + + int dsp; // file handler. + int read_buffer_size; + SAMPLE_TYPE* read_buffer; + FLT* flt_read_buffer; + +# ifdef JACK + jack_port_t *jack_input_port; + jack_client_t *jack_client; + int nframes; +# endif + + // char error_message[100]; + unsigned int real_sample_rate; + + // pthread-related member variables + pthread_t thread_input_read; + pthread_attr_t thread_input_read_attr; + + // indicates whether the audio thread is running + int running; + + // indicates whether the thread was interrupted (by the audio server, not + // by the user) + int interrupted; +}; + +typedef struct _LingotAudioSystemProperties LingotAudioSystemProperties; + +struct _LingotAudioSystemProperties { + + int forced_sample_rate; // tells whether the sample rate can be changed + + int n_sample_rates; // number of available sample rates + int* sample_rates; // sample rates + + int n_devices; // number of available devices + char** devices; // devices +}; + +LingotAudioSystemProperties* lingot_audio_get_audio_system_properties( + audio_system_t audio_system); +void lingot_audio_audio_system_properties_destroy(LingotAudioSystemProperties*); + +// creates an audio handler +LingotAudioHandler + * + lingot_audio_new(audio_system_t audio_system, char* device, + int sample_rate, LingotAudioProcessCallback process_callback, + void *process_callback_arg); + +// destroys an audio handler +void lingot_audio_destroy(LingotAudioHandler*); + +int lingot_audio_start(LingotAudioHandler*); +void lingot_audio_stop(LingotAudioHandler*); + +#endif diff --git a/src/lingot-background.xpm b/src/lingot-background.xpm new file mode 100644 index 0000000..d7cb633 --- /dev/null +++ b/src/lingot-background.xpm @@ -0,0 +1,201 @@ +/* + * 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 + */ +/* XPM */ +static char *background2_xpm[] = { +/* columns rows colors chars-per-pixel */ +"160 100 74 1", +" c #000000", +". c #8E8D8E", +"X c #9A898B", +"o c #9A9294", +"O c #9A999B", +"+ c #A79699", +"@ c #A7989A", +"# c #9F9EA0", +"$ c #A19FA1", +"% c #A7A6A7", +"& c #A8A6A8", +"* c #A7A8A9", +"= c #AAAAAB", +"- c #BFBFBF", +"; c #CD9A9D", +": c #D09B9F", +"> c #DD9EA2", +", c #DDA1A5", +"< c #ECA7AC", +"1 c #EEAAAE", +"2 c #F1A7AE", +"3 c #F0AAAE", +"4 c #E6B3AE", +"5 c #EEADB0", +"6 c #F0ADB0", +"7 c #EEB6B9", +"8 c #E7BDBA", +"9 c #ECBCBE", +"0 c #F1B4B9", +"q c #F1BDBF", +"w c #BFC0BF", +"e c #B5E5AE", +"r c #ABF1AE", +"t c #B2F0B3", +"y c #B7F1B9", +"u c #B8F0BA", +"i c #C0E8BA", +"p c #EEBDC0", +"a c #F0BEC1", +"s c #BFEFC1", +"d c #C2C2C2", +"f c #CDC6C5", +"g c #CCCBCD", +"h c #CECFD0", +"j c #D0CED0", +"k c #CFD0D0", +"l c #D1D1D1", +"z c #DDD6D7", +"x c #D7D6D8", +"c c #D9D7D9", +"v c #DBDADC", +"b c #EEC0C2", +"n c #F0C0C2", +"m c #DDE0DF", +"M c #D9EFDA", +"N c #DDDFE1", +"B c #F0DFE3", +"V c #DEE0E1", +"C c #E2E2E2", +"Z c #EEE2E4", +"A c #EEEEEE", +"S c #F0E2E4", +"D c #F1E7EB", +"F c #F0EBEB", +"G c #E2F0E3", +"H c #E7F1EA", +"J c #EDF0EE", +"K c #EFEFF0", +"L c #F1EFF3", +"P c #EEF3F3", +"I c #F2F2F2", +"U c #F7F8F7", +"Y c #F7F8F8", +"T c #F9F9F9", +/* pixels */ +" ", +" Oggfddgdfdddddd8-dw-8dd---8dd---8dd---8dd---8dd---8dd---8dd---8ddww-8wwdddddd-ddddd8dddddwd-8wddd-8dd---8dd---8dd---8dd---8dd---8dd---8dd---8dd---8dwddgffdggO ", +" gTTTTTTTTTYYYYYYYYYYYUUTYYYYYTYYYUUTYYYYYTYYYUUTYYYYYTUYYUUTYYYYYTTTTTTYTTTTTTTTYYYPYYYTTTTTTTYUYYYYYTUYYUUTYYYYYTUUYTUTYYYYYTUUYUTTYYYYYTYYYUUTYYTTTTTTTTTTTk ", +" fTTTTTTTTTYYYYYYYYYYUUUUYYYYYYYYYYUUYYYYYYYYYYUYYYYYYYYYYYUUYYYYTTTTTTTTTYTTTTTTTTYYYYYTTTTTTTTYYYYYYYYYYUUYYYYYYYYUTUUYYYYYYYYTUTUUYYYYYYYYYYUYYYTTTTTTTTTTTg ", +" fTTTTTTYYYYUUIUYUUULUUUUUUUUUUUUUUUIUIUUUUUUIUUUUUUUUUUUUUUUUUUUUUUUUUTYUYUUUYYUUUYYUUUYYYUUUYYUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUIIUUUUUUUUIUUUUUYTTTTTTTTTTf ", +" gTTTTTTUUYUIUIUIIUYLUUULULUIUUIIULUUIUUUILUIUIULUIIUIIUUUIUUUUUIUUUULIUUUUUUUUUUUULUUUUYYUIYIIUUULUUUIUUULUIUIUUILUILIULULIIUUIIULUUUIIUILUIUIULUIUTTTTTTTTTTg ", +" gTTTTTTUUUUUIUIIIUIUIUIIIUIIULIUIUIIIUIIUIUILIUIUIUIIULUIUIIUUIIUIUUIUULUUUIUIYUUIUUUUIUIUUIUIULUIUUIULUIUIIUUIIUIUUIIIIUIUIILIIUUIIIIUIUIUILIUIUIUIUYUTTTTTTd ", +" dTTTTTTUULUIIIIIUIILIIIUIIIIIIIIILUIIIIIIIIIUIIILIUIILUIULUIIIUUIUILUULLULUUUUIUIUULUUUIUUIUIIUULUIIULUIIIIILIIIIIILIIUILUIIUIIIIILUIIIIIIIIUIIILIIIUIUTTTTTTf ", +" dTTTYYULULLIIIIIIIIIIIILIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIUIIIUIULIUIUIUUIIIUIUIUIIIIUIUIIIIIIIIIIUIIIIFIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIILIIIIIIUUUUYTTTf ", +" dTTYYUUULIIIIFIIIFIIJIIILIIFIIIIIFIJIIIIIIFFIJLILIIIIIIIIIIIIIIIIIIIIIIIIUIIIIIIIUIIIIILUIUIIIIIILUIIIIIIIIIIIIIAIIIIIFILIIIFJIIIFIJIIIIIFIFIJPLIFIIIIIIUYTTTg ", +" dTTUULUUIILIAPIAJJIFIFIFIJIIJFIIAFPIAFFAJIIIIJIIILIIIIIIIIIIIIIUIIIUIUIIULIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIFIIJIIPFFIFIFIJIAIFIJLJJIIPIIKKPIIIIIUIIUYYTTd ", +" fTTUUUULIIIIAAFPFLJAAPFIFFJAIJFAAIIAIIPFIIFIJIIIFILFIIIIIIIIIIIIIIIIIIIIIIIUILUUIIIIUIIIIIIIIIIIILIIIIIIIIIJIIFIIFPIFFPIIJIIIIFKJAPAAFIAAFAAAAAAFIFIFIIUIUTTTd ", +" dTYYUIIIIIFJJAJFAAFAAAAAAAAAFPAAAAFFAFPPPAFFIIJFIIIIFIIIIIIIIJIIIIIIIAvccvczcvxccvcccvcvvFIIIIIIIIIJIJIFIIIIIJIJIALIAAIIFPFAAAFAAAFAAAAAFPAAAAAAFAJIIIILIUUUTd ", +" wYYUIUIILIJIJAAPAAPAAAAAAAAAAAAAAAAJAPFAFFPPFJLFIJIIJJIFIJJIFIJFvzvcxgO....X............OgvcccNFIJJIIIJIIJJJFIJIAJAAAJFAJAAAAAPAAAAAAAAAAAAAAAAAJALJFIFLUIUUTd ", +" wTYIIIIIIIFFJFAAAAAAAAAAAAAAAAAAAAAAPPAIFPAAJIJJIIJJIIJIIIAvzvcgO....OgvcvcvcvxcvccvcccvgO....OgcccvFIIFIIIIFJIJIIFFILJJKPAAAAAAAAAAAAAAAAAAAAAAAAJLAIIIIUUTTd ", +" wYTYUUIIFFIJFIAAAAAAAAAAAAAAAAAAAAPFAFJALJJPIFIIJIFIIFAmzzgO...OgvcvcvAIIIIFLIILIIILIIIIFvvcccg@...OgccmFIFIIIJIFPIIJJAPFFAAAAAAAAAAAAAAAAAAAAAAAAAJIJILIIUUTd ", +" -YYUUUIIFJLAAAAAAAAHAAAAAAAAAAAAAAAFPAJJFIJIFIFJIIJAvvgO..OgvcvvAIIPIPIPYLLIILIILLILULLPLYIIPIKvvcvgO..OgvvAIFPIPAJIAIAAFFPAIAAAAAAAAAAAAAAAAAAAAAAIJJILILUUTd ", +" 8YULUIIIIIJAAAAAAAAAAAAAAAAAAAAPFFPJFIFILAIJIIIIJvcgO.OgccvAIPPIIPIIIIILLLLILLLLILLILLIPLLLILPIIJPIJvcvgO.OgvvAFIIIJIAIPIAAJFAPIJAAAAAAAAAAAAAAAAAIFFIIIIUUTTd ", +" dTUUUUIIIAAAAAAAAAAAAAAAAAAAAAAPAFAIALIIJIPJJJAvgO.OgvvAPIIPPPIIIIIPIKILYLLLLLLIULUIIIIYLYLIIIIIIIPPIPIAvvfO.OgvAJIIFIJJJIIAAFPAFAAAAAAAAAAAAAAAAAFJIIIIIIUUTd ", +" dUTULIIIIIFAAAAAAAAAAAAAAAAAAAAFAPPJIJJAIIFAvvgOOgcvAIPIIIPIIPYIIPIIIIILYLLYLIYILLLLLIIPLLPPIPIIIIPIIIIIPPAvcgOOgcvAJIJLJFIIIFAPFAPAAAAAAAAAAAAAAAIFJFILUUUUTd ", +" -TYUIIIIIFIPAAAAAAAAAZAAAPFAAAAAAJIIIJJIJAvgO.OgvAIIPIIIIPIPPYPLYPPPYLPLLYYLYLLLLLLULYLLPPPPPPPPIPPPPPIPIIPIIPvgO.OgvAJIJIAAIAAPAFFFAAAAAAAAAAAAAAAJIIJIIUUUTd ", +" -YYUIUIIJIAAAAAAAAAAAAAAAAAAAAKLAIFFFIIAvgOOgcvJIPIIIPIPPIPPPPPPPPYPPLYLLLLLLLYLLTLLLYLLYYLPPYPIPPUPUIPPIPIPIPIAvcgOOkvAIJIIAAIFPPAPPFAAAAAAAAAAAAAJFIIIIIUUTd ", +" -YYUIUIIFAAAAAAAAAAAAAAAAFPPFKKJIIFIIAvgOOgvAIPPIIPIPPPPPPIPPPPPYPPPPPPLYLLLTLLTLLTLYLLLPLYPPPPPYPPPPPUIPIPPPPPIIIAvgOOgvAFJJIAPFIFAFPAAAAAAAAHAAAAIILIILUUTTd ", +" 8YYUUIIIIIAAAAAAAAAAAAAFAAPFJJLIFPPJAkOOgvAIIIIIIPIPIPPUIPPYPPPPPPPYPLYLTLTLLLLLLLLYLLYYPPPPYYPPPPPPPPPPIPIPPPYIIIIIAvgOOjAILIJIFPJAPFPAAAAAZAAAAAAFIFLIULUUTd ", +" dYYUUIIIIFJLAAAAAAAAAPFPAFJIPAIFPIAvg%gvAIIPPPYPIPUPPPPPPPYPPPPPPPPDDDBDKHHAHAGMGAHHHHHDDDDDLPPYPPPPPYPPPYPPYPPPIIJIIPJvg%gvAIJJIAIAAJFPFFAAAAAAAAAAFIIIUIUUUd ", +" dYYUIUIIJIAAAAAAAAAAAAAPFAPFIJAJAvgOOjAIPIPPPPPPPPPPPYPYPPPPPPAKKLBa0p0piyuuuutetuuutsu8000pBKDAPPPPPPPPPPPPPPYIPIPPPIIIAlOOgvAIFIAAIAAAAPAAAAAAAAAJIPIIIUUUUd ", +" -TYULIIILIAJAAAAAAAFPPAFAJIIJILAkOOgvAJPIPIPPPPIPPPPPPPPPPAPABb77p022224errrrrrrrrrrrre422210777bZAKIPPPPPPPPPPPPPIPIPIPFAvgOOjAPIFLIFAJFFPAAAAAAAAAIFILUIUUTd ", +" -UYUUUIIIJKFJAAAAAPFAAAIPIFFJAvg%gvAIPPPPIPIPPYPPUPPYPPAAZpp971122222224errrrrrrrrrrrre422222111177bCPPPPPPPPPPPPIPIPPPIPPIAvg%gvAJJJIIPIJAAAAAAAAAAAIIILUUUUd ", +" -UUUIULIIFAAAAAAAAAFPAIPIJJIAkOOgAIPPPPIPIPPPPPPPPPPPPCp775<111111122224errrrrrrrrrrrre42212221<1165777pZAPPPPPUPYPPPPPPIPPIIAgOOjAJAIFFIAAJFPPAAAAAIJIIIUUUTd ", +" 8YTULIIIJIAAAAAAAAAAAIFFFIIAg%gvAPIIIPPPIPPPPPPYPPAPZb7555155<5122212224errrrrrurrrrrre5222211266<<111117pBAAPPPPPPPPPYIPIIPIPAvg%jAIPAIJLJALAFAAAIIALILUIUTTd ", +" dTUUIIIIIFPJAAAAIFPAPJAIAAvg%kAIPPIPIPUPPPUPPPYPPZb77551<5<1<1<1<1227pp8uuuyyysMsyuuuyu87701221<651<11111179pZAPPPPYPPPPPIPPIPIJAk%gvAIIFJIJJPFAJFJJJJIIIUUUTd ", +" dUUUUIUIIIIFAPFJFJAIFIIAAkOOgAIIJIPIPPPPPPPYPPFCb7511155<1115550777pBBDDHHHHHHHKDHHHHHHDDDBp77p75<151111111157bAPPPPPPIPPPPIIJPPIAjOOgAJPIFIFAPFALFILJILIIUUTd ", +" -TYULIIILIJFFJJFPFFIJAAAk%gvAPPJPIPPYPPPPPPPJZb755<5<111<11797pBKDKLPPPYLLYLLLLTLLYLYLPPPPLKDDHBp777655111<11157pZAPPPPPPPPYPPPIPIAvg%kAAJJFIAAAJAAIAIIIUUUUYd ", +" -YYUUIIIIFIJIFJAPIPAIPAg*kAIPPIIIPIPPPPYPPKDb755<<5551<577pZKADPPPPLPPLLLLLLYLLLLLLLLLPLPPPPPPPPAAKCn775511113<<57pBAPPPYPPPPPPPPPIJAl%kAJIPIIAALIFAIFIILIUUTd ", +" -YYULUIIILILAIFFIAIIAAk%jAPPIPIIPPPPPPPPKCb71115111<557nCADPPPPPPPPPPPPLLLLLLLLLLLLLLPLLPLPPPPPPPPPPAABp7551111131179BAPPPPPPPPIIIPPIAg*kAFAFAIJFPIAIIIIUULYYd ", +" 8YYUUIIILIFJIJIJAIAAvf*jAJPIPIPPPIPYPPPHb051111<11577pZLPPPPPPPPPPPPLPKPLLLLLLLLLLLLLLLLPPPPPPPPPPPPPPPKCpp75111<11117pAPPPPYPPPPPPIPJAk&gvAIJAIJAJAIIIILUUYTd ", +" dYYUIIIIJIJJIJIJIFAkOOgAPPIPIIPPPPPPPAZp1111111557pZKKPPPPPPPHPPPPPPKPPLLPLKKKLLLLLKKPLLLPLPPPPPPPPPPPPPPPHSa7411111111pZAPPPPPPPPPPIPPAkOOgAAIIFAIIIIIIUIUTTd ", +" dYYUUUIIIILIFLAJAAk%gvAIJIPIPPPPPPPPAb7511111117pZDPPPPPPPPPPPPPPPPKPKKLLLLKKKLLLLLKKKKLPLPKPKPPHPPPPPPPYPPPFCb7111111157pZPYPPYPPPIPPPPAvg%hAJPIJAIJIIIUUYYTd ", +" -TYUIIIIIJIIFJIAAk%kAIPIPJIPPPPYYPACb511111117pDHPPPPPPPPPPPPPPPKKKPKKPKKKKKKDKKKDLKKKKKPKKKPKHPPPPPPPPPPPPPPPKZp7111111<5pCPPPPPPUPPPPPIPAh&kAFIIIIFIIIIIYYTd ", +" -UYUUULIIIIJPIFAk%gAPIJPIIPPPPIPPHb0611111119ZAPPPPPPHPPPPPPPPPPKKKKKKKKKKKKKKADKKLKKKKKKKKKPKKAPPPPPPPPPPPPPPPPAZp11111555pAPPPPPPPPPPIJIJAg%kAPFIIIIIIYYYTTd ", +" -TYUUUIIIIILIIFvOgAJIIPYPPPYPPPPZp55<551167bZPPPPPPPPPPPHPKPKKKPAKKKAKAKAAKKKAKKKAAKHDKKKKHKKKKAPAKPPKPKPPPPPPPPPPHb711<51157pZPPPPPYPYJPIIPAgOvPAPIIIUIUUUTTd ", +" 8TYUUUIIIIIIFJFgOvIIPPIPPPPPPPPZp11111511aCAPPPPPPPPPPKKPAPAKAKKAKHAKAAKDHKDAAAKKAAAAKKDKAKKKAAAJAPKKKPPHPKPPPPPPPPAZb1<113115pZPPPPPPPIIPJPIvOfAIFIIIIUILUTTd ", +" wTTLUULIIIIIJAk%jAIPJIPPPPPPPPZb511515579ZPYPPPPPPPPPPPKPKKHKKAAAAAAAAAAAAAKKAADKKAHAKAAHAAAAAAAAAAHKKKAAKPPPPPPPPPPPHb755<1111pZPPPPPPPIIPJIAg%jAPIIIILUUUTTd ", +" dYUUUUIIIIIJAk%gAPJIPIPPPPPPPZp15111<5pZAPPPPPPPKPHHPKKAAAKKKKKAAAAAAAAAAAAHKAAKKAADAAAAAAAAAAAAAAAKAAAJPKKKPHPPPPPPPPPZp5111111pDPPPPPPPPPIIPAk%gAIIIIULUUTTd ", +" dYTTUILUIIIFl%kAJIPPPYPPPPPAZ91111155pZPPPPPPPPPKPPAPKHAAKAAAAHAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAKDAAAAAAAAKAKPAJPAPPPPPPPPPZb1111115pBPPYPPPPIPPIPAk*kJIIUIUUUYTd ", +" dTTUUUUIIIAl&kAPIPIPPPPPPPKb75111147bZPPPPHPPPPJPAAAKKAAAAAAADAAAAAHAAAAAHKAAAAHAAAKKAAAKAAAAKHAAAAAAAAAJKKAKAPPPPPPPPPPPZb7111<157bCPPIPPPPPPIPAl&lAIIUUUTTTd ", +" dTTTUUUUIIvOgAPPPUPPPPPPPA91111155pHKPPPPPPPKPPAKJKAAAAAAAAAAAAAAAAAAAAAAAAHKAAKAAADKAAKDAAAAAAAAAAAAAAAAAKKAPJKPHPPPPPPPPPZp51111159ZPPPPPPYPPJPAgOVUILIUUTTf ", +" dTTUUIUIIIjOvIJPPPPPPPPPAb5111115pZPPPPPPPPPPPHPJKJAAAAAAAAAAAAAAAAAAAAKDAAAAKDAAAKKHAAHAAAAAAAHAAAAAAAAAAHJAAAKKPPPPPPPPPPPZp1111115bPPPYPPPPPIIFvOlFUUUUTTTd ", +" dTTTTTUUAl=lAIPPIPPPPPPPZ7111115pAPPPPPPPHPKKAAAPAAAAAAAAHAAAKKAAAAAHKAAAKADAAAAAAHZAAAAAAAAAAAAAKKAAAKAAAAAAKKKAPAKPPPPPPPPPAp5111118BPPPPPPYPIPPAl=lAUTTTTTd ", +" dTTTTYUIl&lAIIIIPPPYPPPZp511115pZPPPPPPKPPKPKAPAHAAAAAAAKKKAAHAAAAAAADKAAHAKAAAAAAAAAAAAAAAAAAAAAADAAAKAAAAAAAKAKHPPHKPKPPPPPPZp511111pZPPPPPPPPPIIIl=xITTTTTd ", +" dTTYYUIC$gPIIIPPIPPPPPAp511111pZPPPPPPPJPPKAAAAAAAAAAAAADAAAAAAKKACAAAKAAAAHAAAAAAAAAAAAAAAAAAKAAAHAAHDHDAAAADHAKKAAKKKKHPPPPPPZb511111bZPPPPYPIPIIIPh$CUTTTTw ", +" dTTTYYIlOVIIIIPIPPYPPAb511115pZPPPPPPPPPKKKAAAAAAAAAAAAAAAAAADAKKKKAAAKAAAAKAAAAAAAAAAAAAAAAAADAAAAAAAAAKAAAAAAAAAAAAPKPKPPPPPPPZp511115bAPPPPPPIIIUICOlITTTTf ", +" dTTTYIx=xAUUIPYYPPYPZp5<5115pHPPPPPPPHPKKAKKAHAAAAAAAAAAHKAKHAAAHAHDKDKAKAAAADKAAAAAAAAAAAAAAAHAAAHAAAAAAAAAAAAAAAAAAAKKPPKPPPPPPKp511155pSPPYPIIUPIUPx=xITTTf ", +" fTTTYC$kPIIPIPPPPPPAp511515pAPPPPPPPPKPKAKHAAKAAAAAAAAAAAAAAAAAKDAAKKHAADAAAAAAAAAAAAAAAAAAAAAAAAKDAAAAAAAAAAAAAAAAAAKAHKKPPPPPPPPAp511555pLPPPPPPIIUIIx$CTTTg ", +" dTTTUl$VUIYPIPPPPPPp515555pCIPPPPPKKPKPAAAAAAAAAAAAAAAHAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAHDAAAAAAAKKAAKDAAAAAAAKAAAAAAHKKKHKPPPPPPPPZp5555<7ZPIPPPIUUIUIC$xUTTf ", +" dTTTC$lPIYIPYYPPYIS7151557ZPPPPPPPPHKAAAAAAAAAAAAAAAAADAKDAAAAKKAAAAAAAAAAKHAAAAAAAAAAAAAAAAAAAAAAAAAKHAAAAAAAKAAAAAAAAKKPPHPPPPPPPPZ75<555pDPYPYPPIIUUIj%CTTd ", +" dTTTC$gCIUIPPIPYPA9535131bJPYPPPPPPPAAAAAAAAAAAAKKAAAAAAAAAAAADAAAAAAAAAHAAAKKAAAAAAAAAAAAAAAAAAAAAAAHKDHKAAADHKZAAAAAAKKKKKAPPPPPPPJb413115aSIYPPYPUPPCh%CTTd ", +" dTTTIc$OlVPYPYPPPn535355nZPPPPPPHKKJAAAAAAAAAAAHDAAAKAAAAHAKHAAAAAAAAAAKKAAAHAAAAAAAAAAAAAAAAAAHAAAAAKDAAKAAAAAAAAAAAAAAAKHKPPAPPPPPPZb613336nFYYPPYPCl$$xITTg ", +" dTTYYIVl#OlVPPIIS0133159AIPPPPPPKPKAAAAAAAAKAAAAAAADKAAAADKAAAAAAAAAAAAAAAAAAAAJAAAAAAADAAAAAAADAAAAAAAAAAAAAAAAAAAHAAAAAAKKHAPJPPPPPPAb113330SPPPPVx##jCITTTf ", +" fTTYYYUIClO#kPPSn333330ZPYPPPPPHPKAAAAAAAAAZAAAAAAAHAAAAAAKAAAAAAAAAAAAAAADAAAAFAAPAAAAAAAHAAAAKKAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAPPPPPYPPS033333qZPPxOOkVIYTTTTd ", +" dTTTTIUUIPVh=kx9333333nAPPPPPJIPKAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAJFJAAAKAAJLFAAAKAAAAAAAAAAAAAAAAKKAAAAAAAAAAAAAAAAAAKAAPPPPPPPPYJn6333339vk=kVPUUUYYTTf ", +" dTTTYUUYIIIPl#o;,<333nSPUYPPPPPPKPAAAAAAAAAAAAAHAAAAAAAAAAKAAAKAAAAAJJLPPFLKAAKKAAJAAIJAKAAAAAAAKHAAAKHHAAAAAAAAAAAAAAAAAAAKPAAJPJIPPPPISn611<>;o#kPIUIIUYYTTd ", +" dTTYYUUIIIPIPNgoX:<30SIPPYPPPPPKPAAPAAAAAAAAAAADAAAHKAAAAHKAKADAAAAPFFAFPAJAAKAIAAAIAAAAAKAKAAAADAAAAADDAAAAAAAAAAAAAAAAAAAAAAPJPPPPYPUUPS01<;XogVPYIIUUUTTTTd ", +" dTTTYUIUIIYPIPKVg@;,9FIPPPPPPIPJKAAAAAAAAAAAAAAKKAADKAAAAKDAHAAAAAAFAPPFAPFIALKFIJPIFAAPKAKAAKKAKAAAAAKAAAAAAHAAAAAAAAAAAAJKAAAJPPPPPPPPIF9,;+gNPPPPIUIUTTTTTd ", +" -TTUUULIIIIIPIIPPhOofvPIPPIPIJPJAPFPFFAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAFPIFIIJLIFIIFIJIFIIALJAAJFPAFJAAAAAAAAAAAAAAAAAAAAAAAPAFFPAAIPIPIPIIPVfookAPIIIIIIIIUUUTTd ", +" 8TTUUIUIIIIIIPIPPKVk#OkVJIPJILIIIAAAAPAAAAAAAAAAAAAAAAAAAAAALJPAAAAAPJJFIIFJJFIIFIAIIFIIAIJAAPFAPFAAAAAAAAAAAAAAAAAAAAAJAAIPAAIIAJIIPPJVkOOhVPPPPPIIIUIUIUUTTd ", +" wTTUUUIIIIIPIPJIPPIHNgOOkJPIIPAJIIPFPAPAAAAAAAAAAAAAAAAAAAAAAAAIFJIFIIJILJJLIIIJIIIIJLJJAFIIIAPFFIAAAAAAAAAAAAAAAAAAAAPALAFFPIAJIPIPIAkO#kVAIPIIPIIIIIILUULTTd ", +" wTTULIUUIIIJPLPPIIPPPPvk=gvAIIJIFFIJAIFAJAAAAAAAAAAAAAAAAAPFJAAJJLJPIFJPJIIJJIIIIJJAIIJIIAIFIJFPJAPAAAAAAAAAAAAAAAAAFAFKJJJIFIJIIJIAvg*hNHPPPIPPIPPIIIIULUUTTd ", +" dYYUIIIIIIPKJPILPIPIPIIKkOOgvAIPIFIJIAJAAFPPFJAAAAAAAAAAAAAAPAIIFIPFIIIIJAvvxvxvxccvvAPIJFIJIIPIJAAKAJAAAAAAAAAAZALAJPAPFIAPPFIIJAvhOOkHPIPPFIIPIJPIIIIUIUUTTd ", +" dYYUUUIIIIJIJFPPFPIPIJIPAvgOOlAIJFPIFLJIAJAFAPFAAAAAPAAAAPFAAFIPIFJPIAvvcgO.........ogvvvAIIJFIFLJAFPAIAAAAAAAAAJAAJAFALJAIFIJIIAjOOgvALPIPPIJLJJLIPIIIIIUUTTd ", +" dYYUIIIIPIAFIFPKAAIIAKIFPPAvg%gvAIIJIIFFIIAFPAPAAAAJFAPALAJFIIJFAJIAvgO..OjccccccxvvgO..OgvAFIJIIIPFAAFFPPFAAPAAAPALFIIJIIAPPIAvg%gvAIFKPLIIIPPFPPFIFPIUIUUUTd ", +" dYYUUIIIIIFPKJJPIJAPFPPIAPPIAgOOfvAIAPIIJJLAPFFAAAAAPFFJJFIPIFAIAvvgoOgccvFJIIJIJIJJAvccgOOgxvAIFFPIIAPPFAAPFFAAJAAJJIJIFJIFAvfOOgAIPIJPLIJAJAAPFAJIPIIIIIUUTd ", +" -YYUUIIIAJLAJAAAAFPPIAIPFIIJIAvgOOgvAFIFPFFIJPAAAAAAAFPAJIPFPAIAjO.OgvAJIIFIJIIIIAFIIIIIAvgO.OjAPPIJIFAAFPFAFPAAPJAIFFIPFIAvgOOgvAIILIPFIFIAAAAJKAIIPJIIIUUUTd ", +" -YYUIUIIIIAAAAAPFAAFJKAIFPAIFJIAvgOOjAIPIIPAIFPIFPAFPIJIFFIAIAvg%gxvAIFIJIIIIIFIIIIFPFIIIFAvcg%fvAAIFIPJIFPAPAIFFIIPIIJPIAlOogvAIJJIFFIIJPJAAAAAJAAFIIIIIUIUTd ", +" -TYULIIIIIPFAAAAAAAFJAJJIFIPPFIPFAvg*fvAFIJIAJIIAJILAFIIIJJFAkOOgAAIPIIIIJJIFIJPFJPIIJIAIIIIIAgOOgAFIIJAIIPPFJAIPIAFPJIAvg%gvAKJJIFJIPJJKAAAAAAAAAAIJIIIIIUTTd ", +" -UYUUUIIIFFPAAAAAAAPAALAAIJFIIAAPIIAhOOgvAKAIIFAIIFJIAIFIJAAk%gvAIIIFJIJIFIIIJLIFIFPFIAJAJIJLAAvg&gAIJFIJIFIFIIIFIFJIAvgoOjAIFPLAIIIFAAPALJAAAAAAAAFLJJILUUUTd ", +" -UUUIULIIIJAAAAAAAAAAAAFPAAJIPFIJAIKAvfOOhALAIJIAAIJFIPIJIAk%kALIJLFIPAIJPIFJIFIPFIAIFIIIAJAJIIIAg%kAJIJFFIIAIAIIIILAgOogvAIJIFIAJJJAFAFPAAAAAAAAAAAIILIIUUUUd ", +" 8YTULIIIFAAAAAAAAAAAAAAAAFPPFAPFAIJKIPAvf*gvAIIFIIIIIIFIFAj%gAIJJIJPIFIJIFFPAAIFAPAAAIJFIFIIAFIIFAj%kAJIIIJIIIFPFAAvg%gvAIIJLPIIIJLJLJAAAAAAAAAAAAAFJJILUIUUTd ", +" dTUUUIIIPFAAAAAAAAAAAAAAAPFAFJKIPJLIFPAIAjOOgvAIIFIPIIIJAk%kAPFIJFIIFIPAFFPAAAAFPIAAIFPPFIPIIJLJIJAk%kAIIILJFIPAAvgOOgAIPAJJIFIJFLJFIAAAAAAAAAAAAAAPPFIIIUUTTd ", +" dUUIUIUIIIAAAAAAAAAAAAAAAAAAPKAAAIFPPIFIFAvgOOgvAIIJIFIAk%kAJIFFIFIJAAFAPALJAAIAAAAAAAPFAAAFIJAIALJAj%kAAJIIIIAvgOOgvAIIFIAIFPAJAAKAJAAAAAAAAAAAAAAFIIIIIIUUTd ", +" -TYULUIILIJFFIFAAAAAAAAAAAAAAAJFFPAFIPLJIIJAvgOOlAIIIFAg%kAIFIPIPIJAAAAAAAAAAAAAAAAAAAAAJALAJAIJIJLFAk%jAIIIJAkOOgvAFIALJAFPIAAAAAJAAAAAAAAAAAAAAAJFILIIIIYYTd ", +" -YYUILIIIJFJJAJAAAAAAAAAAAAAAAAPAPFAIAJIFLJFIAvg%gvAFAj*lAAAIPFFAFAAIIAAAAAAAAAAAAAAAAAAAKJIFIAIFIAIJAj%kAJFmg%gvAIIIJIFIIIAJPFAAAAAAAAAAAAAAAAAAAFJFIJIIUITTd ", +" -YYIUIIIIFIAAAAAAAAAAAAAAAAAAAAAAFJFPAIIPIIIJIIFlOogvvOgAJFIAIJPAFPAAAAAAAAAAAAAAAAAAAAAAAAAAIAALJIJIJAgOcvgOOjAFIJPJFAIIAAPFAFAAAAAAAAAAAAAAAAAAAAIJIIIUUUYTd ", +" 8YYUUUIILIFIAAAAAAAAAAAAAAAAAAAAAAIJAAIAFAJFIFIJAvgOO-OvJPIJIFLFPPFAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAIIFJLvO-oOgvFIIIJFPIIJAAAFAAPAAAAAAAAAAAAAAAAAAAPFIJLLIIUYTd ", +" dYYUUUIIIIAAAAAAAAAAAAAAZAAAAAAAAAAAAJFAPAIIJIFIIIAvg%lAFPFIPAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJJAFIIAIIAk%gvJJIIFIPFIFPFAAPPAAAAAAAAAAAAAAAAAAAAAAAIJIIIIUUTTd ", +" dYYUIIIIIAIAAAAAAAAAAAAAAAAAAAAAAAAAAAPFFAPPAIIJFJIIFmFAIIFFAPFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFFFPPJAIAJFAvAIJJIPIFIPAAIPPFAAAAAAAAAAAAAAAAAAAAAAAAAFIIJIULUTTd ", +" dUYUULIIFJFAAAAPAAAAAAAAAAAAAAAAAAPAAJFPFAFAIAPJIJJFIIIIIFAPAJAPAPAAAAAAAAAAAAAAAAAAAAAAAAAAAJJLJAFAJIFIFJJFIFIFIAJAAAJFPFJAAAAAAAAAAJAAAAAAAAAAAAAFIILIIUUTTd ", +" wYYUIULIIIJLAAAFJAAAAIFAAAJAAAAAJAFAALAAPAIJAIFIJIJIIFIAJIPFIFPFAFAAAAAPFAAAAPFAAJAAAAAAIFJAAFIAAAIIFIIJIIIIIIIIAIAFIPFFAPFAAAAPFAPFAFIAAAAAAAAJALAIFIIIILUTTd ", +" dTTYUIIIIJIFAFPJIFJJIFIAPAIJFJFFFIJIJIIIFIAIIIFIIIIIIIIIIIJJIIIIIPAFIFAAAAFFIFAAFIFIFJJIIFJFIFJJIPFIFIIIFIIFIIFIFIFIFFLIPFIJLAAFJIIJJFJFFJJJFIFFPAJILIIIUUYYTd ", +" fTTYUUUUIILIIIJFIJIJFJIJFIFIIIIIIJFFIFFJIJIIAIIFLLFIFFIIIFIIFIFPFIJIFIIJIPIJIIPJJPIFIIJFJJIIIIJIFIFIPJPIIIIIIIIIIIIIIILIFPIIAIJIIFJFIIJIIIFIIFIIJPFIIIIIUUUTTd ", +" dTTYUUUUUIIIIIFIIIFIIJFIIIIIJIFIIIIIILIIIFIIFPIIIIIIIIIIIIIIFLIIIIFIIFIFPIFIFFIJLJIFILIIIIILFILIIJIIIIIIIIIIIIIIIIIIFLIIJIFIFIJIFFLIIIIIJJIIJFIIIIIIIIIUUTTTTf ", +" dTTTYTTIIIIIIIIFIILIIIIJLIFIIIIJIJIIIILIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIILIIIIIIILIIJILIILJIFIIIILIIIIIJIIIFILIIFIIIIIIIIIIIIIIIIIIIILIJIFIIIIIILIFIIIIIIUTTTTTTd ", +" dTTTTTTUILUIIIILLILIILLIIIILLIIIIIILIIIIIIIIIIIIIUIIILLIIIIIIUIIIIIIIIIIIIIIIIIIILLIIIIIILLIIIUIIIIIIIIIULIIIIIIIIILIIIUIILIIIILIIIILILIIIILLLLIILLUIIUTTTTTTf ", +" dTTTTTTUUULIIIUUIIUIIIUIUIIUIIIIUIIUIIUUIULUIIULUIIIUIUIIIUILIIUIIIIUIIUIIIUILUUIUIIIILUIUIIIILUIULIIIUILIUUIUIIUUIUIUIIIIUUIIIILUIIUIIIIUIUIUIUIIUUUUUTTTTTTd ", +" fTTTTTTTUUUUUUILUIUUUULUIUIULULUIUIULULULIUIUUIULUUUUUIUIUIIUIUIIUUULULIIUUUUILIIUIUUIUIUIIUIIUULLUIUUULUUIUIIUUIIUULULUIULUIUIUUUIUIIUIIIUUIULUILUUUUUTTTTTTg ", +" gTTTTTTTTUUUIUUUUUUIUIUUUUUUIUUUUUUUIUUUUUUUIUUULULUUIUUUUUUUUUUUUIUUUUUUUIUIUUUUUUUIUUUUUUUUUUUUUUUUIUUUUUUUUUIUIUIUUUUUIUUUUULUUUUUUUUUUUIUUUUUUUUTTTTTTTTTf ", +" fTTTTTTTTTTTTYTYYTYUYYUYYUYYYYYUYYUYYUYYTUTYYYYYYYYYYYYUYYUYYUYTYYUYYYUTYYUYYYUTYYUYYYUTYYUYYYUUUYYYYYYYYYUYYUYYYYYUYYUYYUYYTUYUYYUYYUYYTUYUYYUYTTTTTTTTTTTTTg ", +" gTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTg ", +" Oggfggffdddfddddddddddddddddddddddddddddwd8dddddddddddddddddddddddddddddddddddddddddddddddddddd8wddddddddddddddddddddddddddddddddddddddddddddddddddddfgfdgflgO ", +" " +}; diff --git a/src/lingot-complex.c b/src/lingot-complex.c new file mode 100644 index 0000000..6bee5ef --- /dev/null +++ b/src/lingot-complex.c @@ -0,0 +1,71 @@ +/* + * 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 "lingot-complex.h" + +/** + * addition. All parameters are overlapables. + */ +void lingot_complex_add(LingotComplex* a, LingotComplex* b, LingotComplex* c) { + c->r = a->r + b->r; + c->i = a->i + b->i; +} + +/** + * substraction. All parameters are overlapables. + */ +void lingot_complex_sub(LingotComplex* a, LingotComplex* b, LingotComplex* c) { + c->r = a->r - b->r; + c->i = a->i - b->i; +} + +/** + * multiplication. All parameters are overlapables. + */ +void lingot_complex_mul(LingotComplex* a, LingotComplex* b, LingotComplex* c) { + if ((c != a) && (c != b)) { + c->r = a->r * b->r - a->i * b->i; + c->i = a->i * b->r + a->r * b->i; + } else { + LingotComplex r; + r.r = a->r * b->r - a->i * b->i; + r.i = a->i * b->r + a->r * b->i; + *c = r; + } +} + +/** + * division. All parameters are overlapables. + */ +void lingot_complex_div(LingotComplex* a, LingotComplex* b, LingotComplex* c) { + FLT bm2 = b->r * b->r + b->i * b->i; + + if ((c != a) && (c != b)) { + c->r = (a->r * b->r + a->i * b->i) / bm2; + c->i = (a->i * b->r - a->r * b->i) / bm2; + } else { + LingotComplex r; + r.r = (a->r * b->r + a->i * b->i) / bm2; + r.i = (a->i * b->r - a->r * b->i) / bm2; + *c = r; + } +} diff --git a/src/lingot-complex.h b/src/lingot-complex.h new file mode 100644 index 0000000..1fc78cc --- /dev/null +++ b/src/lingot-complex.h @@ -0,0 +1,44 @@ +/*
+ * 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
+ */
+
+#ifndef _LINGOT_COMPLEX_H_
+#define _LINGOT_COMPLEX_H_
+
+#include <math.h>
+#include "lingot-defs.h"
+
+// single complex arithmetic :)
+
+typedef struct _LingotComplex LingotComplex;
+
+struct _LingotComplex
+ {
+ FLT r;
+ FLT i;
+ };
+
+void lingot_complex_add(LingotComplex* a, LingotComplex* b, LingotComplex* c);
+void lingot_complex_sub(LingotComplex* a, LingotComplex* b, LingotComplex* c);
+void lingot_complex_mul(LingotComplex* a, LingotComplex* b, LingotComplex* c);
+void lingot_complex_div(LingotComplex* a, LingotComplex* b, LingotComplex* c);
+
+#endif
diff --git a/src/lingot-config-scale.c b/src/lingot-config-scale.c new file mode 100644 index 0000000..17f0415 --- /dev/null +++ b/src/lingot-config-scale.c @@ -0,0 +1,255 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include "lingot-config-scale.h" + +LingotScale* lingot_config_scale_new() { + + LingotScale* scale = malloc(sizeof(LingotScale)); + + scale->name = NULL; + scale->notes = 0; + scale->note_name = NULL; + scale->offset_cents = NULL; + scale->offset_ratios[0] = NULL; + scale->offset_ratios[1] = NULL; + scale->base_frequency = 0.0; + + return scale; +} + +void lingot_config_scale_allocate(LingotScale* scale, unsigned short int notes) { + scale->notes = notes; + scale->note_name = malloc(notes * sizeof(char*)); + scale->offset_cents = malloc(notes * sizeof(FLT)); + scale->offset_ratios[0] = malloc(notes * sizeof(short int)); + scale->offset_ratios[1] = malloc(notes * sizeof(short int)); +} + +void lingot_config_scale_destroy(LingotScale* scale) { + unsigned short int i; + for (i = 0; i < scale->notes; i++) { + free(scale->note_name[i]); + } + + if (scale->offset_cents != NULL) + free(scale->offset_cents); + + if (scale->offset_ratios[0] != NULL) + free(scale->offset_ratios[0]); + if (scale->offset_ratios[1] != NULL) + free(scale->offset_ratios[1]); + + if (scale->note_name != NULL) + free(scale->note_name); + if (scale->name != NULL) + free(scale->name); + + scale->name = NULL; + scale->notes = 0; + scale->note_name = NULL; + scale->offset_cents = NULL; + scale->offset_ratios[0] = NULL; + scale->offset_ratios[1] = NULL; + scale->base_frequency = 0.0; +} + +void lingot_config_scale_restore_default_values(LingotScale* scale) { + + unsigned short int i; + static char* tone_string[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", + "G#", "A", "A#", "B", }; + + lingot_config_scale_destroy(scale); + + // default 12 tones equal-tempered scale hard-coded + scale->name = strdup("Default equal-tempered scale"); + lingot_config_scale_allocate(scale, 12); + + scale->base_frequency = MID_C_FREQUENCY; + + scale->note_name[0] = strdup(tone_string[0]); + scale->offset_cents[0] = 0.0; + scale->offset_ratios[0][0] = 1; + scale->offset_ratios[1][0] = 1; // 1/1 + + for (i = 1; i < scale->notes; i++) { + scale->note_name[i] = strdup(tone_string[i]); + scale->offset_cents[i] = 100.0 * i; + scale->offset_ratios[0][i] = -1; // not used + scale->offset_ratios[1][i] = -1; // not used + } +} + +void lingot_config_scale_copy(LingotScale* dst, LingotScale* src) { + unsigned short int i; + + lingot_config_scale_destroy(dst); + + *dst = *src; + + dst->name = strdup(src->name); + lingot_config_scale_allocate(dst, dst->notes); + + for (i = 0; i < dst->notes; i++) { + dst->note_name[i] = strdup(src->note_name[i]); + dst->offset_cents[i] = src->offset_cents[i]; + dst->offset_ratios[0][i] = src->offset_ratios[0][i]; + dst->offset_ratios[1][i] = src->offset_ratios[1][i]; + } +} + +int lingot_config_scale_parse_shift(char* char_buffer, double* cents, + short int* numerator, short int* denominator) { + const static char* delim = "/"; + char* char_buffer_pointer1 = strtok(char_buffer, delim); + char* char_buffer_pointer2 = strtok(NULL, delim); + short int num, den; + int result = 1; + + if (numerator != NULL) { + *numerator = -1; + } + + if (denominator != NULL) { + *denominator = -1; + } + + int n = 0; + if (!char_buffer_pointer2) { + n = sscanf(char_buffer_pointer1, "%lf", cents); + if (!n) { + result = 0; + } + } else { + n = sscanf(char_buffer_pointer1, "%hd", &num); + if (!n) { + result = 0; + } else { + n = sscanf(char_buffer_pointer2, "%hd", &den); + if (!n) { + result = 0; + } else { + *cents = 1200.0 * log2(1.0 * num / den); + if (numerator != NULL) { + *numerator = num; + } + if (denominator != NULL) { + *denominator = den; + } + } + } + } + + if (!result) { + *numerator = 1; + *denominator = 1; + *cents = 0.0; + } + + return result; +} + +void lingot_config_scale_format_shift(char* char_buffer, double cents, + short int numerator, short int denominator) { + if (numerator < 0) { + sprintf(char_buffer, "%0.4lf", cents); + } else { + sprintf(char_buffer, "%hd/%hd", numerator, denominator); + } +} + +int lingot_config_scale_load_scl(LingotScale* scale, char* filename) { + FILE* fp; + int i; + char* char_buffer_pointer1; + char* nl; + const static char* delim = " \t\n"; + int result = 1; + +# define MAX_LINE_SIZE 1000 + + char char_buffer[MAX_LINE_SIZE]; + + if ((fp = fopen(filename, "r")) == NULL) { + sprintf(char_buffer, "error opening scale file %s", filename); + perror(char_buffer); + return 0; + } + + scale->base_frequency = MID_C_FREQUENCY; + + fgets(char_buffer, MAX_LINE_SIZE, fp); + if (strchr(char_buffer, '!') != char_buffer) { + fclose(fp); + return 0; + } + + fgets(char_buffer, MAX_LINE_SIZE, fp); + fgets(char_buffer, MAX_LINE_SIZE, fp); + + nl = strrchr(char_buffer, '\r'); + if (nl) + *nl = '\0'; + nl = strrchr(char_buffer, '\n'); + if (nl) + *nl = '\0'; + scale->name = strdup(char_buffer); + + fgets(char_buffer, MAX_LINE_SIZE, fp); + sscanf(char_buffer, "%hu", &scale->notes); + + fgets(char_buffer, MAX_LINE_SIZE, fp); + lingot_config_scale_allocate(scale, scale->notes); + + scale->note_name[0] = strdup("1"); + scale->offset_cents[0] = 0.0; + scale->offset_ratios[0][0] = 1; + scale->offset_ratios[1][0] = 1; // 1/1 + + for (i = 1; i < scale->notes; i++) { + + fgets(char_buffer, MAX_LINE_SIZE, fp); + + char_buffer_pointer1 = strtok(char_buffer, delim); + + int r = lingot_config_scale_parse_shift(char_buffer_pointer1, + &scale->offset_cents[i], &scale->offset_ratios[0][i], + &scale->offset_ratios[1][i]); + if (!r) { + result = 0; + } + + sprintf(char_buffer, "%d", i + 1); + scale->note_name[i] = strdup(char_buffer); + } + + fclose(fp); + +# undef MAX_LINE_SIZE + return result; +} diff --git a/src/lingot-config-scale.h b/src/lingot-config-scale.h new file mode 100644 index 0000000..781d02b --- /dev/null +++ b/src/lingot-config-scale.h @@ -0,0 +1,52 @@ +/* + * 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 + */ + +#ifndef LINGOT_CONFIG_SCALE_H_ +#define LINGOT_CONFIG_SCALE_H_ + +#include "lingot-defs.h" + +typedef struct _LingotScale LingotScale; + +struct _LingotScale { + char* name; // name of the scale + unsigned short int notes; // number of notes + FLT* offset_cents; // offset in cents + short int* offset_ratios[2]; // offset in ratios (pairs of integers) + FLT base_frequency; // frequency of the first note + char** note_name; // note names + + // -- internal parameters -- + + FLT max_offset_rounded; // round version of maximum offset in cents +}; + +LingotScale* lingot_config_scale_new(); +void lingot_config_scale_allocate(LingotScale* scale, unsigned short int notes); +void lingot_config_scale_destroy(LingotScale* scale); +int lingot_config_scale_load_scl(LingotScale* scale, char* filename); +int lingot_config_scale_parse_shift(char*, double*, short int*, short int*); +void lingot_config_scale_format_shift(char*, double, short int, short int); +void lingot_config_scale_copy(LingotScale* dst, LingotScale* src); +void lingot_config_scale_restore_default_values(LingotScale* scale); + +#endif /* LINGOT_CONFIG_SCALE_H_ */ diff --git a/src/lingot-config.c b/src/lingot-config.c new file mode 100644 index 0000000..e0a1343 --- /dev/null +++ b/src/lingot-config.c @@ -0,0 +1,458 @@ +/* + * 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" }; + 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", 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) { + + config->audio_system = AUDIO_SYSTEM_ALSA; + sprintf(config->audio_dev[AUDIO_SYSTEM_OSS], "%s", "/dev/dsp"); + sprintf(config->audio_dev[AUDIO_SYSTEM_ALSA], "%s", "plughw:0"); + + 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) { + *((audio_system_t*) param) = AUDIO_SYSTEM_ALSA; + 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 +} diff --git a/src/lingot-config.h b/src/lingot-config.h new file mode 100644 index 0000000..157baa6 --- /dev/null +++ b/src/lingot-config.h @@ -0,0 +1,123 @@ +/* + * 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 + */ + +#ifndef __LINGOT_CONFIG_H__ +#define __LINGOT_CONFIG_H__ + +#include "lingot-defs.h" +#include "lingot-config-scale.h" + +typedef enum audio_system_t { + AUDIO_SYSTEM_OSS = 0, AUDIO_SYSTEM_ALSA = 1, AUDIO_SYSTEM_JACK = 2 +} audio_system_t; + +typedef enum window_type_t { + NONE = 0, RECTANGULAR = 1, HANNING = 2, HAMMING = 3 +} window_type_t; + + +typedef struct _LingotConfig LingotConfig; + +// Configuration struct. Determines the tuner behaviour. +// Some parameters are internal only. +struct _LingotConfig { + + audio_system_t audio_system; + + char audio_dev[3][80]; + int sample_rate; // soundcard sample rate. + unsigned int oversampling; // oversampling factor. + + // root_frequency_reference_note_t root_frequency_referente_note; + FLT root_frequency_error; // deviation of the above root frequency. + + FLT min_frequency; // minimum valid frequency. + + unsigned int fft_size; // number of samples of the FFT. + + FLT calculation_rate; + FLT visualization_rate; + + FLT temporal_window; // duration in seconds of the temporal window. + + // samples stored in the temporal window (internal parameter). + unsigned int temporal_buffer_size; + + FLT noise_threshold_db; // dB + FLT noise_threshold_nu; // natural units (internal parameter) + + window_type_t window_type; + + // frequency finding algorithm configuration + //------------------------------------------- + + unsigned int peak_number; // number of maximum peaks considered. + + // number of adjacent samples needed to consider a peak. + unsigned int peak_half_width; + + /* maximum amplitude relation between principal and secondary peaks. + The max peak doesn't has to be the fundamental frequency carrier if it + has an amplitude relation with the fundamental considered peak lower than + this parameter. */ + FLT peak_rejection_relation_db; // dBs + FLT peak_rejection_relation_nu; // natural units (internal) + + FLT gain; // dBs + FLT gain_nu; // natural units (internal) + + // DFT approximation + unsigned int dft_number; // number of DFTs. + unsigned int dft_size; // samples of each DFT. + + // max iterations for Newton-Raphson algorithm. + unsigned int max_nr_iter; + + //---------------------------------------------------------------------------- + + // gauge rest value. (gauge contemplates [-0.5, 0.5]) + FLT gauge_rest_value; + + //---------------------------------------------------------------------------- + + LingotScale* scale; +}; + +// converts an audio_system_t to a string +const char* audio_system_t_to_str(audio_system_t audio_system); +// converts a string to an audio_system_t +audio_system_t str_to_audio_system_t(char* audio_system); + +LingotConfig* lingot_config_new(); +void lingot_config_destroy(LingotConfig*); +void lingot_config_copy(LingotConfig* dst, LingotConfig* src); + +// back to default configuration. +void lingot_config_restore_default_values(LingotConfig*); + +// derivate internal parameters from external ones. +void lingot_config_update_internal_params(LingotConfig*); + +void lingot_config_save(LingotConfig*, char* filename); +void lingot_config_load(LingotConfig*, char* filename); + +#endif // __LINGOT_CONFIG_H__ diff --git a/src/lingot-core.c b/src/lingot-core.c new file mode 100644 index 0000000..a7554a6 --- /dev/null +++ b/src/lingot-core.c @@ -0,0 +1,535 @@ +/* + * 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 <stdio.h> +#include <math.h> +#include <sys/soundcard.h> +#include <string.h> +#include <errno.h> +#include <sys/time.h> +#include <time.h> +#include <stdlib.h> + +#include "lingot-complex.h" + +#include "lingot-fft.h" +#include "lingot-signal.h" +#include "lingot-core.h" +#include "lingot-config.h" +#include "lingot-i18n.h" +#include "lingot-msg.h" + +int +lingot_core_read_callback(FLT* read_buffer, int read_buffer_size, void *arg); + +void lingot_core_run_reading_thread(LingotCore* core); +void lingot_core_run_computation_thread(LingotCore* core); + +int decimation_input_index = 0; + +LingotCore* lingot_core_new(LingotConfig* conf) { + + char buff[1000]; + LingotCore* core = malloc(sizeof(LingotCore)); + + core->conf = conf; + core->running = 0; + core->audio = NULL; + core->spd_fft = NULL; + core->X = NULL; + core->spd_dft = NULL; + core->diff2_spd_fft = NULL; + core->flt_read_buffer = NULL; + core->temporal_buffer = NULL; + core->windowed_temporal_buffer = NULL; + core->windowed_fft_buffer = NULL; + core->hamming_window_temporal = NULL; + core->hamming_window_fft = NULL; + core->antialiasing_filter = NULL; + + int requested_sample_rate = conf->sample_rate; + + if (conf->sample_rate <= 0) { + conf->sample_rate = 0; + } + + core->audio = lingot_audio_new(conf->audio_system, + conf->audio_dev[conf->audio_system], conf->sample_rate, + (LingotAudioProcessCallback) lingot_core_read_callback, core); + + if (core->audio != NULL) { + + if (requested_sample_rate != core->audio->real_sample_rate) { + conf->sample_rate = core->audio->real_sample_rate; + lingot_config_update_internal_params(conf); + sprintf( + buff, + _("The requested sample rate is not available, the real sample rate has been set to %d Hz"), + core->audio->real_sample_rate); + lingot_msg_add_warning(buff); + } + + if (conf->temporal_buffer_size < conf->fft_size) { + conf->temporal_window = ((double) conf->fft_size + * conf->oversampling) / conf->sample_rate; + conf->temporal_buffer_size = conf->fft_size; + lingot_config_update_internal_params(conf); + sprintf( + buff, + _( + "The temporal buffer is smaller than FFT size. It has been increased to %0.3f seconds"), + conf->temporal_window); + lingot_msg_add_warning(buff); + } + + // since the SPD is simmetrical, we only store the 1st half. + if (core->conf->fft_size > 256) { + core->spd_fft = malloc((core->conf->fft_size >> 1) * sizeof(FLT)); + core->X = malloc((core->conf->fft_size >> 1) * sizeof(FLT)); + memset(core->spd_fft, 0, (core->conf->fft_size >> 1) * sizeof(FLT)); + memset(core->X, 0, (core->conf->fft_size >> 1) * sizeof(FLT)); + } else { // if the fft size is 256, we store the whole signal for representation. + core->spd_fft = malloc((core->conf->fft_size) * sizeof(FLT)); + core->X = malloc((core->conf->fft_size) * sizeof(FLT)); + memset(core->spd_fft, 0, core->conf->fft_size * sizeof(FLT)); + memset(core->X, 0, (core->conf->fft_size) * sizeof(FLT)); + } + + core->spd_dft = malloc((core->conf->dft_size) * sizeof(FLT)); + memset(core->spd_dft, 0, core->conf->dft_size * sizeof(FLT)); + + core->diff2_spd_fft = malloc(core->conf->fft_size * sizeof(FLT)); // 2nd derivative from SPD. + memset(core->diff2_spd_fft, 0, core->conf->fft_size * sizeof(FLT)); + + memset(core->spd_dft, 0, core->conf->dft_size * sizeof(FLT)); + + lingot_fft_create_phase_factors(conf); // creates the phase factors for FFT. + core->fft_out = malloc((core->conf->fft_size) * sizeof(LingotComplex)); // complex signal in freq domain. + memset(core->fft_out, 0, core->conf->fft_size * sizeof(LingotComplex)); + + // audio source read in floating point format. + core->flt_read_buffer = malloc(core->audio->read_buffer_size + * sizeof(FLT)); + memset(core->flt_read_buffer, 0, core->audio->read_buffer_size + * sizeof(FLT)); + + // stored samples. + core->temporal_buffer = malloc((core->conf->temporal_buffer_size) + * sizeof(FLT)); + memset(core->temporal_buffer, 0, core->conf->temporal_buffer_size + * sizeof(FLT)); + + core->hamming_window_temporal = NULL; + core->hamming_window_fft = NULL; + + if (conf->window_type != NONE) { + core->hamming_window_temporal = malloc( + (core->conf->temporal_buffer_size) * sizeof(FLT)); + core->hamming_window_fft = malloc((core->conf->fft_size) + * sizeof(FLT)); + + lingot_signal_window(core->conf->temporal_buffer_size, + core->hamming_window_temporal, conf->window_type); + lingot_signal_window(core->conf->fft_size, + core->hamming_window_fft, conf->window_type); + } + + core->windowed_temporal_buffer = malloc( + (core->conf->temporal_buffer_size) * sizeof(FLT)); + memset(core->windowed_temporal_buffer, 0, + core->conf->temporal_buffer_size * sizeof(FLT)); + core->windowed_fft_buffer + = malloc((core->conf->fft_size) * sizeof(FLT)); + memset(core->windowed_fft_buffer, 0, core->conf->fft_size * sizeof(FLT)); + + /* + * 8 order Chebyshev filters, with wc=0.9/i (normalized respect to + * Pi). We take 0.9 instead of 1 to leave a 10% of safety margin, + * in order to avoid aliased frequencies near to w=Pi, due to non + * ideality of the filter. + * + * The cut frequencies wc=Pi/i, with i=1..20, correspond with the + * oversampling factor, avoiding aliasing at decimation. + * + * Why Chebyshev filters?, for a given order, those filters yield + * abrupt falls than other ones as Butterworth, making the most of + * the order. Although Chebyshev filters affects more to the phase, + * it doesn't matter due to the analysis is made on the signal + * power distribution (only module). + */ + core->antialiasing_filter = lingot_filter_cheby_design(8, 0.5, 0.9 + / core->conf->oversampling); + + pthread_mutex_init(&core->temporal_buffer_mutex, NULL); + + // ------------------------------------------------------------ + + core->running = 1; + } + + core->freq = 0.0; + return core; +} + +// ----------------------------------------------------------------------- + +/* Deallocate resources */ +void lingot_core_destroy(LingotCore* core) { + + if (core->audio != NULL) { + lingot_fft_destroy_phase_factors(); // destroy phase factors. + free(core->fft_out); + + lingot_audio_destroy(core->audio); + + free(core->spd_fft); + free(core->X); + free(core->spd_dft); + free(core->diff2_spd_fft); + free(core->flt_read_buffer); + free(core->temporal_buffer); + + if (core->hamming_window_fft != NULL) { + free(core->hamming_window_temporal); + } + + if (core->windowed_temporal_buffer != NULL) { + free(core->windowed_temporal_buffer); + } + + if (core->hamming_window_fft != NULL) { + free(core->hamming_window_fft); + } + + if (core->windowed_fft_buffer != NULL) { + free(core->windowed_fft_buffer); + } + + if (core->antialiasing_filter != NULL) + lingot_filter_destroy(core->antialiasing_filter); + + pthread_mutex_destroy(&core->temporal_buffer_mutex); + } + + free(core); +} + +// ----------------------------------------------------------------------- + +// reads a new piece of signal from audio source, apply filtering and +// decimation and appends it to the buffer +int lingot_core_read_callback(FLT* read_buffer, int read_buffer_size, void *arg) { + + unsigned int i, decimation_output_index; // loop variables. + int decimation_output_len; + FLT* decimation_in; + FLT* decimation_out; + LingotCore* core = (LingotCore*) arg; + LingotConfig* conf = core->conf; + + memcpy(core->flt_read_buffer, read_buffer, read_buffer_size * sizeof(FLT)); + + // double freq = 100.0; + // for (i = 0; i < read_buffer_size; i++) { + // core->flt_read_buffer[i] = 5e2 * cos(2.0 * M_PI * freq * i + // / conf->sample_rate); + // } + + if (conf->gain_nu != 1.0) { + for (i = 0; i < read_buffer_size; i++) + core->flt_read_buffer[i] *= conf->gain_nu; + } + + // + // just readed: + // + // ---------------------------- + // |bxxxbxxxbxxxbxxxbxxxbxxxbxxx| + // ---------------------------- + // + // <----------------------------> read_buffer_size*oversampling + // + + decimation_output_len = 1 + (read_buffer_size + - (decimation_input_index + 1)) / conf->oversampling; + + pthread_mutex_lock(&core->temporal_buffer_mutex); + + /* we shift the temporal window to leave a hollow where place the new piece + of data read. The buffer is actually a queue. */ + if (conf->temporal_buffer_size > decimation_output_len) { + memmove(core->temporal_buffer, + &core->temporal_buffer[decimation_output_len], + (conf->temporal_buffer_size - decimation_output_len) + * sizeof(FLT)); + } + + // + // previous buffer situation: + // + // ------------------------------------------ + // | xxxxxxxxxxxxxxxxxxxxxx | yyyyy | aaaaaaa | + // ------------------------------------------ + // <------> read_buffer_size + // <---------------> fft_size + // <----------------------------------------> temporal_buffer_size + // + // new situation: + // + // ------------------------------------------ + // | xxxxxxxxxxxxxxxxyyyyaa | aaaaa | | + // ------------------------------------------ + // + + // decimation with lowpass filtering + + /* we decimate the read signal and put it at the end of the buffer. */ + if (conf->oversampling > 1) { + + decimation_in = core->flt_read_buffer; + decimation_out = &core->temporal_buffer[conf->temporal_buffer_size + - decimation_output_len]; + + // low pass filter to avoid aliasing. + lingot_filter_filter(core->antialiasing_filter, read_buffer_size, + decimation_in, decimation_in); + + // compression. + for (decimation_output_index = 0; decimation_input_index + < read_buffer_size; decimation_output_index++, decimation_input_index + += conf->oversampling) + decimation_out[decimation_output_index] + = decimation_in[decimation_input_index]; + decimation_input_index -= read_buffer_size; + } else + memcpy(&core->temporal_buffer[conf->temporal_buffer_size + - decimation_output_len], core->flt_read_buffer, + decimation_output_len * sizeof(FLT)); + // + // ------------------------------------------ + // | xxxxxxxxxxxxxxxxyyyyaa | aaaaa | bbbbbbb | + // ------------------------------------------ + // + + pthread_mutex_unlock(&core->temporal_buffer_mutex); + + return 0; +} + +void lingot_core_compute_fundamental_fequency(LingotCore* core) { + + register unsigned int i, k; // loop variables. + LingotConfig* conf = core->conf; + FLT delta_w_FFT = 2.0 * M_PI / conf->fft_size; // FFT resolution in rads. + + // ----------------- TRANSFORMATION TO FREQUENCY DOMAIN ---------------- + + FLT _1_N2 = 1.0 / (conf->fft_size * conf->fft_size); + // SPD normalization constant + + //printf("%d samples transformed of a total of %d\n", conf->fft_size, + // conf->temporal_buffer_size); + + pthread_mutex_lock(&core->temporal_buffer_mutex); + + // windowing + if (conf->window_type != NONE) { + for (i = 0; i < conf->fft_size; i++) { + core->windowed_fft_buffer[i] + = core->temporal_buffer[conf->temporal_buffer_size + - conf->fft_size + i] * core->hamming_window_fft[i]; + } + } else { + memmove(core->windowed_fft_buffer, + &core->temporal_buffer[conf->temporal_buffer_size + - conf->fft_size], conf->fft_size * sizeof(FLT)); + } + + // transformation. + lingot_fft_fft(core->windowed_fft_buffer, core->fft_out, conf->fft_size); + + // esteem of SPD from FFT. (normalized squared module) + for (i = 0; i < ((conf->fft_size > 256) ? (conf->fft_size >> 1) : 256); i++) + core->spd_fft[i] = (core->fft_out[i].r * core->fft_out[i].r + + core->fft_out[i].i * core->fft_out[i].i) * _1_N2; + + // representable piece + memcpy(core->X, core->spd_fft, ((conf->fft_size > 256) ? (conf->fft_size + >> 1) : 256) * sizeof(FLT)); + + // truncated 2nd derivative esteem, to enhance peaks + core->diff2_spd_fft[0] = 0.0; + for (i = 1; i < (conf->fft_size >> 1) - 1; i++) { + core->diff2_spd_fft[i] = 2.0 * core->spd_fft[i] - core->spd_fft[i - 1] + - core->spd_fft[i + 1]; // centred 2nd order derivative, to avoid group delay. + if (core->diff2_spd_fft[i] < 0.0) + core->diff2_spd_fft[i] = 0.0; // truncation + } + + // peaks searching in that signal. + int Mi = lingot_signal_get_fundamental_peak(conf, core->spd_fft, + core->diff2_spd_fft, (conf->fft_size >> 1)); // take the fundamental peak. + + if (Mi == (signed) (conf->fft_size >> 1)) { + core->freq = 0.0; + pthread_mutex_unlock(&core->temporal_buffer_mutex); + return; + } + + FLT w = (Mi - 1) * delta_w_FFT; + // frequency of sample previous to the peak + + // Approximation to fundamental frequency by selective DFTs + // --------------------------------------------------------- + + FLT d_w = delta_w_FFT; + for (k = 0; k < conf->dft_number; k++) { + + d_w = 2.0 * d_w / (conf->dft_size - 1); // resolution in rads. + + if (k == 0) { + lingot_fft_spd(core->windowed_fft_buffer, conf->fft_size, w + d_w, + d_w, &core->spd_dft[1], conf->dft_size - 2); + core->spd_dft[0] = core->spd_fft[Mi - 1]; + core->spd_dft[conf->dft_size - 1] = core->spd_fft[Mi + 1]; // 2 samples known. + } else + lingot_fft_spd(core->windowed_fft_buffer, conf->fft_size, w, d_w, + core->spd_dft, conf->dft_size); + + lingot_signal_get_max(core->spd_dft, conf->dft_size, &Mi); // search the maximum. + + w += (Mi - 1) * d_w; // previous sample to the peak. + } + + w += d_w; // DFT approximation. + + // windowing + if (conf->window_type != NONE) { + for (i = 0; i < conf->temporal_buffer_size; i++) { + core->windowed_temporal_buffer[i] = core->temporal_buffer[i] + * core->hamming_window_temporal[i]; + } + } else { + memmove(core->windowed_temporal_buffer, core->temporal_buffer, + conf->temporal_buffer_size * sizeof(FLT)); + } + + pthread_mutex_unlock(&core->temporal_buffer_mutex); + + // Maximum finding by Newton-Raphson + // ----------------------------------- + + FLT wk = -1.0e5; + FLT wkm1 = w; + // first iterator set to the current approximation. + FLT d1_SPD, d2_SPD; + + for (k = 0; (k < conf->max_nr_iter) && (fabs(wk - wkm1) > 1.0e-8); k++) { + wk = wkm1; + + // ! we use the WHOLE temporal window for greater precission. + lingot_fft_spd_diffs(core->windowed_temporal_buffer, + conf->temporal_buffer_size, wk, &d1_SPD, &d2_SPD); + //printf("%f %f %f\n", wk, d1_SPD, d2_SPD); + wkm1 = wk - d1_SPD / d2_SPD; + } + + w = wkm1; // frequency in rads. + core->freq = (w * conf->sample_rate) / (2.0 * M_PI * conf->oversampling); // analog frequency. +} + +/* start running the core in another thread */ +void lingot_core_start(LingotCore* core) { + + int audio_status = 0; + decimation_input_index = 0; + + if (core->audio != NULL) { + audio_status = lingot_audio_start(core->audio); + + if (audio_status == 0) { + pthread_mutex_init(&core->thread_computation_mutex, NULL); + pthread_cond_init(&core->thread_computation_cond, NULL); + + pthread_attr_init(&core->thread_computation_attr); + pthread_create(&core->thread_computation, + &core->thread_computation_attr, + (void* (*)(void*)) lingot_core_run_computation_thread, core); + } + + core->running = 1; + } +} + +/* stop running the core */ +void lingot_core_stop(LingotCore* core) { + void* thread_result; + + if (core->running == 1) { + core->running = 0; + + // threads cancelation + pthread_cancel(core->thread_computation); + pthread_join(core->thread_computation, &thread_result); + // printf("%p %p %i\n", thread_result, PTHREAD_CANCELED, thread_result + // == PTHREAD_CANCELED); + + pthread_attr_destroy(&core->thread_computation_attr); + + memset(core->X, 0, + ((core->conf->fft_size > 256) ? (core->conf->fft_size >> 1) + : core->conf->fft_size) * sizeof(FLT)); + core->freq = 0.0; + } + + if (core->audio != NULL) + lingot_audio_stop(core->audio); +} + +/* run the core */ +void lingot_core_run_computation_thread(LingotCore* core) { + struct timeval t, tout; + struct timespec tspec; + + gettimeofday(&tout, NULL); + t.tv_sec = 0; + t.tv_usec = 1e6 / core->conf->calculation_rate; + + while (core->running) { + lingot_core_compute_fundamental_fequency(core); + timeradd(&t, &tout, &tout); + tspec.tv_sec = tout.tv_sec; + tspec.tv_nsec = 1000 * tout.tv_usec; + pthread_cond_timedwait(&core->thread_computation_cond, + &core->thread_computation_mutex, &tspec); + + if (core->audio != NULL) { + if (core->audio->interrupted) { + memset(core->X, 0, + ((core->conf->fft_size > 256) ? (core->conf->fft_size + >> 1) : core->conf->fft_size) * sizeof(FLT)); + core->freq = 0.0; + core->running = 0; + } + } + } +} diff --git a/src/lingot-core.h b/src/lingot-core.h new file mode 100644 index 0000000..d4434f6 --- /dev/null +++ b/src/lingot-core.h @@ -0,0 +1,92 @@ +/* + * 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 + */ + +#ifndef __LINGOT_CORE_H__ +#define __LINGOT_CORE_H__ + +#include <pthread.h> + +#include "lingot-defs.h" +#include "lingot-complex.h" +#include "lingot-filter.h" +#include "lingot-config.h" + +#include "lingot-audio.h" + +typedef struct _LingotCore LingotCore; + +struct _LingotCore { + + // -- shared data -- + FLT freq; // analog frequency calculated. + FLT* X; // visual portion of FFT. + // -- shared data -- + + LingotAudioHandler* audio; // audio handler. + + FLT* flt_read_buffer; + FLT* temporal_buffer; // sample memory. + + // precomputed hamming windows + FLT* hamming_window_temporal; + FLT* hamming_window_fft; + + // windowed signals + FLT* windowed_temporal_buffer; + FLT* windowed_fft_buffer; + + // spectral power distribution esteem. + FLT* spd_fft; + FLT* spd_dft; + FLT* diff2_spd_fft; + + LingotComplex* fft_out; // complex signal in freq. + + LingotFilter* antialiasing_filter; // antialiasing filter for decimation. + + int running; + + LingotConfig* conf; // configuration structure + + pthread_t thread_computation; + pthread_attr_t thread_computation_attr; + pthread_cond_t thread_computation_cond; + pthread_mutex_t thread_computation_mutex; + + pthread_mutex_t temporal_buffer_mutex; + +}; + +//---------------------------------------------------------------- + +LingotCore* lingot_core_new(LingotConfig*); +void lingot_core_destroy(LingotCore*); + +// start process +void lingot_core_start(LingotCore*); + +// stop process +void lingot_core_stop(LingotCore*); + +//int lingot_core_read(LingotCore* core); + +#endif //__LINGOT_CORE_H__ diff --git a/src/lingot-defs.h b/src/lingot-defs.h new file mode 100644 index 0000000..a3b077c --- /dev/null +++ b/src/lingot-defs.h @@ -0,0 +1,67 @@ +/* + * 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 + */ + +#ifndef __LINGOT_DEFS_H__ +#define __LINGOT_DEFS_H__ + +#include <sys/types.h> +#include "../config.h" + +// floating point precission. +#define FLT double + +#define SAMPLE_TYPE int16_t +#define SAMPLE_FORMAT AFMT_S16_LE + +#define CONFIG_DIR_NAME ".lingot/" +#define DEFAULT_CONFIG_FILE_NAME "lingot.conf" +extern char CONFIG_FILE_NAME[]; + +#define QUICK_MESSAGES + +#define GTK_EVENTS_RATE 20.0 +#define GAUGE_RATE 60.0 +#define ERROR_DISPATCH_RATE 5.0 + +#define MID_A_FREQUENCY 440.0 +#define MID_C_FREQUENCY 261.625565 + +/* object forward declaration */ +typedef struct _LingotMainFrame LingotMainFrame; + +// optionally we can use the following libraries +//#define LIB_FFTW +//#define LIBSNDOBJ + +// simple try-catch simulation, do not use throw inside loops nor nest try-catch +// blocks +#define try exception = 0; do +#define throw(a) {exception = a;break;} +#define catch while (0); if (exception != 0) + +// this option allows us to throw exception from loops, it contains a goto +// statement, but totally controlled, and it fails to indent the code. +//#define try exception = 0;do +//#define throw(a) {exception = a;goto catch_label;} +//#define catch while (0);catch_label: if (exception != 0) + +#endif diff --git a/src/lingot-fft.c b/src/lingot-fft.c new file mode 100644 index 0000000..4d21b0a --- /dev/null +++ b/src/lingot-fft.c @@ -0,0 +1,167 @@ +/*
+ * 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 <math.h>
+#include <stdlib.h>
+
+#include "lingot-fft.h"
+#include "lingot-config.h"
+
+/*
+ DFT functions.
+ */
+
+#ifndef LIB_FFTW
+
+#include "lingot-complex.h"
+
+// phase factor table, for FFT optimization.
+LingotComplex* wn = NULL;
+
+// creates the phase factor table.
+void lingot_fft_create_phase_factors(LingotConfig* conf) {
+ FLT alpha;
+ unsigned int i;
+
+ wn = (LingotComplex*) malloc((conf->fft_size >> 1) * sizeof(LingotComplex));
+
+ for (i = 0; i < (conf->fft_size >> 1); i++) {
+ alpha = -2.0 * i * M_PI / conf->fft_size;
+ wn[i].r = cos(alpha);
+ wn[i].i = sin(alpha);
+ }
+}
+
+void lingot_fft_destroy_phase_factors() {
+ if (wn != NULL) {
+ free(wn);
+ }
+}
+
+//------------------------------------------------------------------------
+
+// Fast Fourier Transform.
+void _lingot_fft_fft(FLT* in, LingotComplex* out, unsigned long int N,
+ unsigned long int offset, unsigned long int d1, unsigned long int step) {
+ LingotComplex X1, X2;
+ unsigned long int Np2 = (N >> 1); // N/2
+ register unsigned long int a, b, c, q;
+
+ if (N == 2) { // butterfly for N = 2;
+
+ X1.r = in[offset];
+ X1.i = 0.0;
+ X2.r = in[offset + step];
+ X2.i = 0.0;
+
+ lingot_complex_add(&X1, &X2, &out[d1]);
+ lingot_complex_sub(&X1, &X2, &out[d1 + Np2]);
+
+ return;
+ }
+
+ _lingot_fft_fft(in, out, Np2, offset, d1, step << 1);
+ _lingot_fft_fft(in, out, Np2, offset + step, d1 + Np2, step << 1);
+
+ for (q = 0, c = 0; q < (N >> 1); q++, c += step) {
+
+ a = q + d1;
+ b = a + Np2;
+
+ X1 = out[a];
+ lingot_complex_mul(&out[b], &wn[c], &X2);
+ lingot_complex_add(&X1, &X2, &out[a]);
+ lingot_complex_sub(&X1, &X2, &out[b]);
+ }
+}
+
+void lingot_fft_fft(FLT* in, LingotComplex* out, unsigned long int N) {
+ _lingot_fft_fft(in, out, N, 0, 0, 1);
+}
+
+#endif
+
+//------------------------------------------------------------------------
+
+
+/* Spectral Power Distribution esteem, selectively in frequency, by DFT.
+ transforms signal in of N1 samples from frequency wi, with sample
+ separation of dw rads, storing the result on buffer out with N2 samples. */
+void lingot_fft_spd(FLT* in, int N1, FLT wi, FLT dw, FLT* out, int N2) {
+ FLT Xr, Xi;
+ FLT wn;
+ FLT N1_2 = N1 * N1;
+ register int i, n;
+
+ for (i = 0; i < N2; i++) {
+
+ Xr = 0.0;
+ Xi = 0.0;
+
+ for (n = 0; n < N1; n++) { // O(n1*n2) :(
+
+ wn = (wi + dw * i) * n;
+ Xr = Xr + cos(wn) * in[n];
+ Xi = Xi - sin(wn) * in[n];
+ }
+
+ out[i] = (Xr * Xr + Xi * Xi) / N1_2; // normalized squared module.
+ }
+}
+
+//------------------------------------------------------------------------
+
+/*
+ Evaluates 1st and 2nd derivatives from SPD at frequency w.
+ */
+void lingot_fft_spd_diffs(FLT* in, int N, FLT w, FLT* out_d1, FLT* out_d2) {
+ FLT x_cos_wn;
+ FLT x_sin_wn;
+ register int n;
+
+ FLT SUM_x_sin_wn = 0.0;
+ FLT SUM_x_cos_wn = 0.0;
+ FLT SUM_x_n_sin_wn = 0.0;
+ FLT SUM_x_n_cos_wn = 0.0;
+ FLT SUM_x_n2_sin_wn = 0.0;
+ FLT SUM_x_n2_cos_wn = 0.0;
+
+ for (n = 0; n < N; n++) {
+
+ x_cos_wn = in[n] * cos(w * n);
+ x_sin_wn = in[n] * sin(w * n);
+
+ SUM_x_sin_wn += x_sin_wn;
+ SUM_x_cos_wn += x_cos_wn;
+ SUM_x_n_sin_wn += x_sin_wn * n;
+ SUM_x_n_cos_wn += x_cos_wn * n;
+ SUM_x_n2_sin_wn += x_sin_wn * n * n;
+ SUM_x_n2_cos_wn += x_cos_wn * n * n;
+ }
+
+ FLT N_2 = N * N;
+ *out_d1 = 2.0 * (SUM_x_sin_wn * SUM_x_n_cos_wn - SUM_x_cos_wn
+ * SUM_x_n_sin_wn) / N_2;
+ *out_d2 = 2.0 * (SUM_x_n_cos_wn * SUM_x_n_cos_wn - SUM_x_sin_wn
+ * SUM_x_n2_sin_wn + SUM_x_n_sin_wn * SUM_x_n_sin_wn - SUM_x_cos_wn
+ * SUM_x_n2_cos_wn) / N_2;
+}
diff --git a/src/lingot-fft.h b/src/lingot-fft.h new file mode 100644 index 0000000..6195da0 --- /dev/null +++ b/src/lingot-fft.h @@ -0,0 +1,51 @@ +/*
+ * 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
+ */
+
+#ifndef _LINGOT_FFT_H_
+#define _LINGOT_FFT_H_
+
+/*
+ Fourier transforms.
+ */
+
+#include "lingot-defs.h"
+
+#ifndef LIB_FFTW
+
+# include "lingot-complex.h"
+# include "lingot-config.h"
+
+void lingot_fft_create_phase_factors(LingotConfig* conf);
+void lingot_fft_destroy_phase_factors();
+
+// Fast Fourier Transform implementation.
+void lingot_fft_fft(FLT* in, LingotComplex* out, unsigned long int N);
+
+#endif // not LIB_FFTW
+
+// Spectral Power Distribution (SPD) esteem, selectively in frequency.
+void lingot_fft_spd(FLT* in, int N1, FLT wi, FLT dw, FLT* out, int N2);
+
+// first and second SPD derivatives at frequency w.
+void lingot_fft_spd_diffs(FLT* in, int N, FLT w, FLT* out_d1, FLT* out_d2);
+
+#endif
diff --git a/src/lingot-filter.c b/src/lingot-filter.c new file mode 100644 index 0000000..abc499b --- /dev/null +++ b/src/lingot-filter.c @@ -0,0 +1,243 @@ +/* + * 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 <memory.h> +#include <math.h> + +#include "lingot-complex.h" +#include "lingot-filter.h" + +#define max(a,b) (((a)<(b))?(b):(a)) + +// given each polynomial order and coefs, with optional initial status. +LingotFilter* lingot_filter_new(unsigned int Na, unsigned int Nb, FLT* a, + FLT* b) { + unsigned int i; + LingotFilter* filter = malloc(sizeof(LingotFilter)); + filter->N = max(Na, Nb); + + filter->a = malloc((filter->N + 1) * sizeof(FLT)); + filter->b = malloc((filter->N + 1) * sizeof(FLT)); + filter->s = malloc((filter->N + 1) * sizeof(FLT)); + + for (i = 0; i < filter->N + 1; i++) + filter->a[i] = filter->b[i] = filter->s[i] = 0.0; + + memcpy(filter->a, a, (Na + 1) * sizeof(FLT)); + memcpy(filter->b, b, (Nb + 1) * sizeof(FLT)); + + for (i = 0; i < filter->N + 1; i++) { + filter->a[i] /= a[0]; // polynomial normalization. + filter->b[i] /= a[0]; + } + + return filter; +} + +void lingot_filter_destroy(LingotFilter* filter) { + free(filter->a); + free(filter->b); + free(filter->s); + + free(filter); +} + +// Digital Filter Implementation II, in & out overlapables. +void lingot_filter_filter(LingotFilter* filter, unsigned int n, FLT* in, + FLT* out) { + FLT w, y; + register unsigned int i; + register int j; + + for (i = 0; i < n; i++) { + + w = in[i]; + y = 0.0; + + for (j = filter->N - 1; j >= 0; j--) { + w -= filter->a[j + 1] * filter->s[j]; + y += filter->b[j + 1] * filter->s[j]; + filter->s[j + 1] = filter->s[j]; + } + + y += w * filter->b[0]; + filter->s[0] = w; + + out[i] = y; + } +} + +// single sample filtering +FLT lingot_filter_filter_sample(LingotFilter* filter, FLT in) { + FLT result; + + lingot_filter_filter(filter, 1, &in, &result); + return result; +} + +// vector prod +void lingot_filter_vector_product(int n, LingotComplex* vector, + LingotComplex* result) { + register int i; + LingotComplex aux1; + + result->r = 1.0; + result->i = 0.0; + + for (i = 0; i < n; i++) { + aux1.r = -vector[i].r; + aux1.i = -vector[i].i; + lingot_complex_mul(result, &aux1, result); + } + +} + +// Chebyshev filters +LingotFilter* lingot_filter_cheby_design(unsigned int n, FLT Rp, FLT wc) { + int i; // loops + int k; + int p; + + FLT a[n + 1]; + FLT b[n + 1]; + + FLT new_a[n + 1]; + FLT new_b[n + 1]; + + // locate poles + LingotComplex pole[n]; + + for (i = 0; i < n; i++) { + pole[i].r = 0.0; + pole[i].i = 0.0; + } + + FLT T = 2.0; + // 2Hz + FLT W = 2.0 / T * tan(M_PI * wc / T); + + FLT epsilon = sqrt(pow(10.0, 0.1 * Rp) - 1); + FLT v0 = asinh(1 / epsilon) / n; + + FLT sv0 = sinh(v0); + FLT cv0 = cosh(v0); + + FLT t; + + for (i = -(n - 1), k = 0; k < n; i += 2, k++) { + t = M_PI * i / (2.0 * n); + pole[k].r = -sv0 * cos(t); + pole[k].i = cv0 * sin(t); + } + + LingotComplex gain; + + lingot_filter_vector_product(n, pole, &gain); + + if ((n & 1) == 0) {// even + FLT f = pow(10.0, -0.05 * Rp); + gain.r *= f; + gain.i *= f; + } + + FLT f = pow(W, n); + gain.r *= f; + gain.i *= f; + + for (i = 0; i < n; i++) { + pole[i].r *= W; + pole[i].i *= W; + } + + // bilinear transform + LingotComplex sp[n]; + + for (i = 0; i < n; i++) { + sp[i].r = (2.0 - pole[i].r * T) / T; + sp[i].i = (0.0 - pole[i].i * T) / T; + } + + LingotComplex tmp1; + LingotComplex aux2; + + lingot_filter_vector_product(n, sp, &tmp1); + + lingot_complex_div(&gain, &tmp1, &gain); + + for (i = 0; i < n; i++) { + tmp1.r = (2.0 + pole[i].r * T); + tmp1.i = (0.0 + pole[i].i * T); + aux2.r = (2.0 - pole[i].r * T); + aux2.i = (0.0 - pole[i].i * T); + lingot_complex_div(&tmp1, &aux2, &pole[i]); + } + + // compute filter coefficients from pole/zero values + a[0] = 1.0; + b[0] = 1.0; + new_a[0] = 1.0; + new_b[0] = 1.0; + + for (i = 1; i <= n; i++) { + a[i] = 0.0; + b[i] = 0.0; + new_a[i] = 0.0; + new_b[i] = 0.0; + } + + if ((n & 1) == 1) // odd + { + // first subfilter is first order + a[1] = -pole[n / 2].r; + b[1] = 1.0; + } + + // iterate over the conjugate pairs + for (p = 0; p < n / 2; p++) { + FLT b1 = 2.0; + FLT b2 = 1.0; + + FLT a1 = -2.0 * pole[p].r; + FLT a2 = pole[p].r * pole[p].r + pole[p].i * pole[p].i; + + // 2nd order subfilter per each pair + new_a[1] = a[1] + a1 * a[0]; + new_b[1] = b[1] + b1 * b[0]; + + // poly multiplication + for (i = 2; i <= n; i++) { + new_a[i] = a[i] + a1 * a[i - 1] + a2 * a[i - 2]; + new_b[i] = b[i] + b1 * b[i - 1] + b2 * b[i - 2]; + } + for (i = 1; i <= n; i++) { + a[i] = new_a[i]; + b[i] = new_b[i]; + } + } + + gain.r = fabs(gain.r); + for (i = 0; i <= n; i++) { + b[i] *= gain.r; + } + + return lingot_filter_new(n, n, a, b); +} diff --git a/src/lingot-filter.h b/src/lingot-filter.h new file mode 100644 index 0000000..6da7c99 --- /dev/null +++ b/src/lingot-filter.h @@ -0,0 +1,65 @@ +/* + * 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 + */ + +#ifndef __LINGOT_FILTER_H__ +#define __LINGOT_FILTER_H__ + +#include <fcntl.h> +#include <stdlib.h> + +#include "lingot-defs.h" + +/* + digital filtering implementation. + */ + +typedef struct _LingotFilter LingotFilter; + +struct _LingotFilter + { + + FLT* a; + FLT* b; // coefs + FLT* s; // status + + unsigned int N; + + }; + +LingotFilter + * lingot_filter_new(unsigned int Na, unsigned int Nb, FLT* a, FLT* b); + +/** + * Design a Chebyshev type I low pass filter with Rp dB of pass band ripple + * with cutoff pi*wc radians. + */ +LingotFilter* lingot_filter_cheby_design(unsigned int order, FLT Rp, FLT wc); + +void lingot_filter_destroy(LingotFilter*); + +// Digital Filter Implementation II, in & out overlapables. Vector filtering +void lingot_filter_filter(LingotFilter*, unsigned int n, FLT* in, FLT* out); + +// sample filtering +FLT lingot_filter_filter_sample(LingotFilter*, FLT in); + +#endif diff --git a/src/lingot-gauge.c b/src/lingot-gauge.c new file mode 100644 index 0000000..4e5a17f --- /dev/null +++ b/src/lingot-gauge.c @@ -0,0 +1,91 @@ +/* + * 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 "lingot-gauge.h" + +LingotGauge* lingot_gauge_new(FLT initial_position) + { + + // + // ----- ERROR GAUGE FILTER CONFIGURATION ----- + // + // dynamic model of the gauge: + // + // 2 + // d d + // --- s(t) = k (e(t) - s(t)) - q -- s(t) + // 2 dt + // dt + // + // acceleration of gauge position 's(t)' linealy depends on the difference + // respect to the input stimulus 'e(t)' (destination position). Inserting + // a friction coefficient 'q', acceleration proportionaly diminish with + // velocity (typical friction in mechanics). 'k' is the adaptation constant, + // and depends on the gauge mass. + // + // with the following derivatives approximation (valid for high sample rate): + // + // d + // -- s(t) ~= (s[n] - s[n - 1])*fs + // dt + // + // 2 + // d 2 + // --- s(t) ~= (s[n] - 2*s[n - 1] + s[n - 2])*fs + // 2 + // dt + // + // we can obtain a difference equation, and implement it with an IIR digital + // filter. + // + + FLT k = 60; + // adaptation constant. + FLT q = 6; + // friction coefficient. + + FLT a[] = + { + k + GAUGE_RATE*(q + GAUGE_RATE), + -GAUGE_RATE*(q + 2.0*GAUGE_RATE), + GAUGE_RATE*GAUGE_RATE}; + FLT b[] = + { k}; + + LingotGauge* gauge = malloc(sizeof(LingotGauge)); + + gauge->filter = lingot_filter_new( 2, 0, a, b ); + lingot_gauge_compute(gauge, initial_position); + return gauge; + } + +void lingot_gauge_destroy(LingotGauge* gauge) + { + lingot_filter_destroy(gauge->filter); + free(gauge); + } + +void lingot_gauge_compute(LingotGauge* gauge, FLT sample) + { + gauge->position = lingot_filter_filter_sample(gauge->filter, sample); + } + diff --git a/src/lingot-gauge.h b/src/lingot-gauge.h new file mode 100644 index 0000000..cda38c5 --- /dev/null +++ b/src/lingot-gauge.h @@ -0,0 +1,45 @@ +/* + * 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 + */ + +#ifndef __LINGOT_GAUGE_H__ +#define __LINGOT_GAUGE_H__ + +#include "lingot-defs.h" +#include "lingot-filter.h" + +/* + * Implements the dynamic behaviour of the gauge with a digital filter. + */ + +typedef struct _LingotGauge LingotGauge; + +struct _LingotGauge + { + LingotFilter* filter; + FLT position; + }; + +LingotGauge* lingot_gauge_new(FLT); +void lingot_gauge_destroy(LingotGauge*); +void lingot_gauge_compute(LingotGauge*, FLT); + +#endif /*__LINGOT_GAUGE_H__*/ diff --git a/src/lingot-gui-config-dialog-scale.c b/src/lingot-gui-config-dialog-scale.c new file mode 100644 index 0000000..844701b --- /dev/null +++ b/src/lingot-gui-config-dialog-scale.c @@ -0,0 +1,671 @@ +/* + * 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 <stdlib.h> +#include <string.h> +#include <glade/glade.h> +#include <math.h> + +#include "lingot-gui-config-dialog.h" +#include "lingot-gui-config-dialog-scale.h" +#include "lingot-msg.h" +#include "lingot-i18n.h" + +enum { + COLUMN_NAME = 0, COLUMN_SHIFT = 1, COLUMN_FREQUENCY = 2, NUM_COLUMNS = 3 +}; + +void lingot_gui_config_dialog_scale_tree_add_row_tree(gpointer data, + GtkTreeView *treeview) { + GtkTreeModel *model; + GtkTreeStore *model_store; + GtkTreeIter iter1, iter2; + gdouble freq; + + model = gtk_tree_view_get_model(treeview); + model_store = (GtkTreeStore *) model; + + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + + if (gtk_tree_selection_count_selected_rows(selection) == 0) { + gtk_tree_store_append(model_store, &iter2, NULL); + } else { + GList *list = gtk_tree_selection_get_selected_rows(selection, &model); + + int ipath = atoi(gtk_tree_path_to_string(list->data)); + GString *fixed_path = g_string_new(""); + g_string_printf(fixed_path, "%d", ipath); + + GtkTreePath *path = gtk_tree_path_new_from_string(fixed_path->str); + g_string_free(fixed_path, TRUE); + + gboolean valid = FALSE; + + if (path) { + if (gtk_tree_model_get_iter(model, &iter1, path)) { // get iter from specified path + valid = TRUE; + } else { // invalid path + // g_error("Error!!!\n"); + // TODO + } + gtk_tree_path_free(path); + } else { + // g_error("Error!!!\n"); + // TODO + } + + g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL); + g_list_free(list); + + if (!valid) + return; + + if (ipath == 0) { + lingot_msg_add_warning( + _("You cannot insert before the reference note.")); + return; + } + + // g_free(path_str); + gtk_tree_store_insert_before(model_store, &iter2, NULL, &iter1); + } + + gtk_tree_model_get_iter_first(model, &iter1); + gtk_tree_model_get(model, &iter1, COLUMN_FREQUENCY, &freq, -1); + + gtk_tree_store_set(model_store, &iter2, COLUMN_NAME, "?", COLUMN_SHIFT, + "1/1", COLUMN_FREQUENCY, freq, -1); +} + +void lingot_gui_config_dialog_scale_tree_remove_selected_items(gpointer data, + GtkTreeView *treeview) { + GtkTreeStore *store; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + + if (gtk_tree_selection_count_selected_rows(selection) == 0) + return; + + GList *list = gtk_tree_selection_get_selected_rows(selection, &model); + store = GTK_TREE_STORE(model); + + int nRemoved = 0; + while (list) { + int ipath = atoi(gtk_tree_path_to_string(list->data)); + ipath -= nRemoved; + GString *fixed_path = g_string_new(""); + g_string_printf(fixed_path, "%d", ipath); + + GtkTreePath *path = gtk_tree_path_new_from_string(fixed_path->str); + g_string_free(fixed_path, TRUE); + if (path) { + if (ipath != 0) { + if (gtk_tree_model_get_iter(model, &iter, path)) { // get iter from specified path + gtk_tree_store_remove(store, &iter); // remove item + nRemoved++; + } else { // invalid path + // g_error("Error!!!\n"); + // TODO + } + } + gtk_tree_path_free(path); + } else { + // g_error("Error!!!\n"); + // TODO + } + list = list->next; + } + g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL); + g_list_free(list); +} + +void lingot_gui_config_dialog_scale_tree_cell_edited_callback( + GtkCellRendererText *cell, gchar *path_string, gchar *new_text, + gpointer user_data) { + GtkTreeView *treeview; + GtkTreeModel *model; + GtkTreeStore *model_store; + GtkTreeIter iter, iter2; + GtkCellRenderer *renderer; + char* shift_char; + char* stored_shift_char; + gdouble freq, stored_freq, base_freq, stored_shift; + double shift_cents; + short int shift_numerator, shift_denominator; + gdouble shiftf2, freq2; + char* char_pointer; + char buff[80]; + const char* delim = " \t\n"; + LingotConfigDialog* config_dialog = (LingotConfigDialog*) user_data; + int index; + + treeview = config_dialog->scale_treeview; + model_store = (GtkTreeStore *) gtk_tree_view_get_model(treeview); + + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + + if (gtk_tree_selection_count_selected_rows(selection) != 1) + return; + + GList *list = gtk_tree_selection_get_selected_rows(selection, &model); + // model_store = GTK_TREE_STORE ( model ); + + int ipath = atoi(gtk_tree_path_to_string(list->data)); + GString *fixed_path = g_string_new(""); + g_string_printf(fixed_path, "%d", ipath); + + GtkTreePath *path = gtk_tree_path_new_from_string(fixed_path->str); + g_string_free(fixed_path, TRUE); + + gboolean valid = FALSE; + + if (path) { + if (gtk_tree_model_get_iter(model, &iter, path)) { // get iter from specified path + valid = TRUE; + } else { // invalid path + // g_error("Error!!!\n"); + // TODO + } + gtk_tree_path_free(path); + } else { + // g_error("Error!!!\n"); + // TODO + } + + g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL); + g_list_free(list); + + if (!valid) + return; + + renderer = &cell->parent; + guint column_number = GPOINTER_TO_UINT(g_object_get_data( + G_OBJECT(renderer), "my_column_num")); + + switch (column_number) { + + case COLUMN_NAME: + if (strchr(new_text, ' ') || strchr(new_text, '\t')) { + lingot_msg_add_warning( + _("Do not use space characters for the note names.")); + } else if (strchr(new_text, '\n') || strchr(new_text, '{') || strchr( + new_text, '}')) { + lingot_msg_add_warning(_("The name contains illegal characters.")); + } else { + char_pointer = strtok(new_text, delim); + gtk_tree_store_set(model_store, &iter, COLUMN_NAME, (char_pointer + == NULL) ? "?" : new_text, -1); + } + break; + + case COLUMN_SHIFT: + + lingot_config_scale_parse_shift(new_text, &shift_cents, + &shift_numerator, &shift_denominator); + + // TODO: full validation + + if ((ipath == 0) && (fabs(shift_cents - 0.0) > 1e-10)) { + lingot_msg_add_warning( + _("You cannot change the first shift, it must be 1/1.")); + break; + } + + if (isnan(shift_cents) || (shift_cents <= 0.0 - 1e-10) || (shift_cents + > 1200.0)) { + lingot_msg_add_warning( + _("The shift must be between 0 and 1200 cents, or between 1/1 and 2/1.")); + break; + } + + gtk_tree_model_get(model, &iter, COLUMN_SHIFT, &stored_shift_char, + COLUMN_FREQUENCY, &stored_freq, -1); + lingot_config_scale_parse_shift(stored_shift_char, &stored_shift, NULL, + NULL); + free(stored_shift_char); + lingot_config_scale_format_shift(buff, shift_cents, shift_numerator, + shift_denominator); + gtk_tree_store_set(model_store, &iter, COLUMN_SHIFT, buff, + COLUMN_FREQUENCY, stored_freq * pow(2.0, (shift_cents + - stored_shift) / 1200.0), -1); + break; + + case COLUMN_FREQUENCY: + + if (!strcmp(new_text, "mid-A")) { + freq = MID_A_FREQUENCY; + } else if (!strcmp(new_text, "mid-C")) { + freq = MID_C_FREQUENCY; + } else { + sscanf(new_text, "%lg", &freq); + // TODO: validation + } + + gtk_tree_model_get(model, &iter, COLUMN_SHIFT, &shift_char, + COLUMN_FREQUENCY, &stored_freq, -1); + lingot_config_scale_parse_shift(shift_char, &shift_cents, NULL, NULL); + free(shift_char); + + freq *= pow(2.0, -gtk_spin_button_get_value_as_float( + config_dialog->root_frequency_error) / 1200.0); + base_freq = freq * pow(2.0, -shift_cents / 1200.0); + + gtk_tree_model_get_iter_first(model, &iter2); + + index = 0; + do { + gtk_tree_model_get(model, &iter2, COLUMN_SHIFT, &shift_char, -1); + lingot_config_scale_parse_shift(shift_char, &shiftf2, NULL, NULL); + free(shift_char); + freq2 = base_freq * pow(2.0, shiftf2 / 1200.0); + gtk_tree_store_set(model_store, &iter2, COLUMN_FREQUENCY, (index + == ipath) ? freq : freq2, -1); + index++; + } while (gtk_tree_model_iter_next(model, &iter2)); + + //gtk_spin_button_set_value(config_dialog->root_frequency_error, 0.0); + + break; + } + +} + +void lingot_gui_config_dialog_scale_tree_frequency_cell_data_function( + GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, + GtkTreeIter *iter, gpointer user_data) { + gdouble freq; + gchar buf[20]; + LingotConfigDialog* config_dialog = (LingotConfigDialog*) user_data; + + gtk_tree_model_get(model, iter, COLUMN_FREQUENCY, &freq, -1); + freq *= pow(2.0, gtk_spin_button_get_value_as_float( + config_dialog->root_frequency_error) / 1200.0); + + if (fabs(freq - MID_A_FREQUENCY) < 1e-3) { + g_object_set(renderer, "text", "mid-A", NULL); + } else if (fabs(freq - MID_C_FREQUENCY) < 1e-3) { + g_object_set(renderer, "text", "mid-C", NULL); + } else { + g_snprintf(buf, sizeof(buf), "%.4f", freq); + g_object_set(renderer, "text", buf, NULL); + } +} + +void lingot_gui_config_dialog_scale_tree_add_column( + LingotConfigDialog* config_dialog) { + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeView *treeview = config_dialog->scale_treeview; + + column = gtk_tree_view_column_new(); + + gtk_tree_view_column_set_title(column, _("Name")); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, "text", COLUMN_NAME, + NULL); + g_object_set(renderer, "editable", TRUE, NULL); + g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER( + COLUMN_NAME)); + g_signal_connect( + renderer, + "edited", + (GCallback) lingot_gui_config_dialog_scale_tree_cell_edited_callback, + config_dialog); + + gtk_tree_view_append_column(treeview, column); + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Shift")); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, "text", COLUMN_SHIFT, + NULL); + g_object_set(renderer, "editable", TRUE, NULL); + g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER( + COLUMN_SHIFT)); + // gtk_tree_view_column_set_cell_data_func(column, renderer, + // lingot_config_dialog_scale_tree_shift_cell_data_function, NULL, + // NULL); + + g_signal_connect( + renderer, + "edited", + (GCallback) lingot_gui_config_dialog_scale_tree_cell_edited_callback, + config_dialog); + + gtk_tree_view_append_column(treeview, column); + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Frequency [Hz]")); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, "text", + COLUMN_FREQUENCY, NULL); + g_object_set(renderer, "editable", TRUE, NULL); + g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER( + COLUMN_FREQUENCY)); + gtk_tree_view_column_set_cell_data_func(column, renderer, + lingot_gui_config_dialog_scale_tree_frequency_cell_data_function, + config_dialog, NULL); + + g_signal_connect( + renderer, + "edited", + (GCallback) lingot_gui_config_dialog_scale_tree_cell_edited_callback, + config_dialog); + + gtk_tree_view_append_column(treeview, column); +} + +int lingot_gui_config_dialog_scale_validate(LingotConfigDialog* dialog, + LingotScale* scale) { + + GtkTreeIter iter, iter2; + GtkTreeModel* model = gtk_tree_view_get_model(dialog->scale_treeview); + + char* name; + char* name2; + char* shift_char; + gdouble shift, last_shift; + int empty_names = 0; + + gtk_tree_model_get_iter_first(model, &iter); + + last_shift = -1.0; + + // TODO: full validation + + int row1 = 0; + + do { + gtk_tree_model_get(model, &iter, COLUMN_NAME, &name, COLUMN_SHIFT, + &shift_char, -1); + lingot_config_scale_parse_shift(shift_char, &shift, NULL, NULL); + free(shift_char); + + if (!strcmp(name, "") || !strcmp(name, "?")) { + empty_names = 1; + } + + gtk_tree_model_get_iter_first(model, &iter2); + int row2 = 0; + do { + gtk_tree_model_get(model, &iter2, COLUMN_NAME, &name2, -1); + if ((row1 != row2) && !strcmp(name, name2)) { + lingot_msg_add_error(_("There are notes with the same name")); + // TODO: select the conflictive line + free(name); + free(name2); + return 0; + } + free(name2); + row2++; + } while (gtk_tree_model_iter_next(model, &iter2)); + + free(name); + + if (shift < last_shift) { + lingot_msg_add_error( + _("There are invalid values in the scale: the notes should be ordered by frequency / shift")); + // TODO: select the conflictive line + return 0; + } + + if (shift >= 1200.0) { + lingot_msg_add_error( + _("There are invalid values in the scale: all the notes should be in the same octave")); + return 0; + } + + last_shift = shift; + row1++; + } while (gtk_tree_model_iter_next(model, &iter)); + + if (empty_names) { + lingot_msg_add_warning(_("There are notes without name")); + return 0; + } + + return 1; +} + +void lingot_gui_config_dialog_scale_apply(LingotConfigDialog* dialog, + LingotScale* scale) { + GtkTreeIter iter; + GtkTreeModel* model = gtk_tree_view_get_model(dialog->scale_treeview); + gdouble freq, shift; + short int shift_num, shift_den; + char* shift_char; + gchar* name; + int i = 0; + + int rows = gtk_tree_model_iter_n_children(model, NULL); + + gtk_tree_model_get_iter_first(model, &iter); + gtk_tree_model_get(model, &iter, COLUMN_FREQUENCY, &freq, -1); + + scale->name = strdup(gtk_entry_get_text(dialog->scale_name)); + scale->base_frequency = freq; + lingot_config_scale_allocate(scale, rows); + + do { + gtk_tree_model_get(model, &iter, COLUMN_NAME, &name, COLUMN_SHIFT, + &shift_char, -1); + lingot_config_scale_parse_shift(shift_char, &shift, &shift_num, + &shift_den); + free(shift_char); + + scale->note_name[i] = name; + scale->offset_cents[i] = shift; + scale->offset_ratios[0][i] = shift_num; + scale->offset_ratios[1][i] = shift_den; + i++; + } while (gtk_tree_model_iter_next(model, &iter)); +} + +void lingot_gui_config_dialog_scale_rewrite(LingotConfigDialog* dialog, + LingotScale* scale) { + gtk_entry_set_text(dialog->scale_name, scale->name); + GtkTreeStore* store = (GtkTreeStore *) gtk_tree_view_get_model( + dialog->scale_treeview); + gtk_tree_store_clear(store); + GtkTreeIter iter2; + char buff[80]; + + int i; + for (i = 0; i < scale->notes; i++) { + gtk_tree_store_append(store, &iter2, NULL); + FLT freq = scale->base_frequency * pow(2.0, scale->offset_cents[i] + / 1200.0); + lingot_config_scale_format_shift(buff, scale->offset_cents[i], + scale->offset_ratios[0][i], scale->offset_ratios[1][i]); + gtk_tree_store_set(store, &iter2, COLUMN_NAME, scale->note_name[i], + COLUMN_SHIFT, buff, COLUMN_FREQUENCY, freq, -1); + } +} + +void lingot_gui_config_dialog_import_scl(gpointer data, + LingotConfigDialog* config_dialog) { + GtkWidget * dialog = gtk_file_chooser_dialog_new(_("Open Scale File"), + GTK_WINDOW(config_dialog->win), GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT, NULL); + GtkFileFilter *filefilter; + filefilter = gtk_file_filter_new(); + static gchar* filechooser_last_folder = NULL; + + gtk_file_filter_set_name(filefilter, (const gchar *) _("Scala files")); + gtk_file_filter_add_pattern(filefilter, "*.scl"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filefilter); + + if (filechooser_last_folder != NULL) { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), + filechooser_last_folder); + } + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + if (filechooser_last_folder != NULL) + free(filechooser_last_folder); + filechooser_last_folder = strdup(gtk_file_chooser_get_current_folder( + GTK_FILE_CHOOSER(dialog))); + // TODO + LingotScale* scale = lingot_config_scale_new(); + if (!lingot_config_scale_load_scl(scale, filename)) { + lingot_msg_add_error(_( + "The scale cannot be imported: file format error")); + lingot_config_scale_destroy(scale); + free(scale); + } else { + lingot_gui_config_dialog_scale_rewrite(config_dialog, scale); + } + + g_free(filename); + } + gtk_widget_destroy(dialog); + //g_free(filefilter); +} + +gint lingot_gui_config_dialog_scale_key_press_cb(GtkWidget *widget, + GdkEventKey *kevent, gpointer data) { + + LingotConfigDialog* dialog = (LingotConfigDialog*) data; + if (kevent->type == GDK_KEY_PRESS) { + if (kevent->keyval == 0xffff) { + g_signal_emit_by_name(G_OBJECT(dialog->button_scale_del), + "clicked", NULL); + } else if (kevent->keyval == 0xff63) { + g_signal_emit_by_name(G_OBJECT(dialog->button_scale_add), + "clicked", NULL); + } + } + + return TRUE; +} + +gint lingot_gui_config_dialog_scale_tree_view_column_get_index( + GtkTreeViewColumn *column) { + GtkTreeView *tree = GTK_TREE_VIEW(column->tree_view); + GList *cols = gtk_tree_view_get_columns(tree); + int counter = 0; + + while (cols != NULL) { + if (column == GTK_TREE_VIEW_COLUMN(cols->data)) { + g_list_free(cols); + return counter; + } + cols = cols->next; + counter++; + } + + g_list_free(cols); + return -1; +} + +gboolean lingot_gui_config_dialog_scale_table_query_tooltip(GtkWidget *widget, + gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, + gpointer user_data) { + GtkTreePath* path; + GtkTreeViewColumn* col; + gint cx, cy; + + gtk_tree_view_get_path_at_pos((GtkTreeView*) widget, x, y, &path, &col, + &cx, &cy); + + gint column_index = + lingot_gui_config_dialog_scale_tree_view_column_get_index(col); + + switch (column_index) { + case COLUMN_NAME: + gtk_tooltip_set_text( + tooltip, + _("Note identifier, free text that will be displayed in the main window when tuning close to the given note. Don't use space characters here.")); + break; + case COLUMN_SHIFT: + gtk_tooltip_set_text( + tooltip, + _("Shift. You can define it as a deviation in cents from the reference note (the first one), or as a frequency ratio, like '3/2' or '5/4'. All the values must be between 0 and 1200 cents, or between 1/1 and 2/1 (i.e., all the notes must be in the same octave), and they must be well ordered.")); + break; + case COLUMN_FREQUENCY: + gtk_tooltip_set_text( + tooltip, + _("Frequency. You can enter here the absolute frequency for a given note as a reference, and all the other frequencies will shift according to the deviations specified in the 'Shift' column. You can use an absolute numeric value or the keywords 'mid-C' (261.625565 Hz) and 'mid-A' (440 Hz).")); + break; + } + + return TRUE; +} + +void lingot_gui_config_dialog_scale_show(LingotConfigDialog* dialog, + GladeXML* _gladeXML) { + + dialog->scale_name = GTK_ENTRY( + glade_xml_get_widget(_gladeXML, "scale_name")); + GtkWidget* scroll = glade_xml_get_widget(_gladeXML, "scrolledwindow1"); + + /* crea el modelo del arbol */ + GtkTreeStore *model_store = gtk_tree_store_new(3, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_DOUBLE); + GtkTreeModel* model = GTK_TREE_MODEL(model_store); + + /* crea un nuevo widget gtktreeview */ + dialog->scale_treeview = GTK_TREE_VIEW(gtk_tree_view_new()); + + /* agrega columnas al modelo del arbol */ + lingot_gui_config_dialog_scale_tree_add_column(dialog); + + /* asocia el modelo al gtkteeview */ + gtk_tree_view_set_model(dialog->scale_treeview, model); + gtk_tree_selection_set_mode(gtk_tree_view_get_selection( + dialog->scale_treeview), GTK_SELECTION_MULTIPLE); + + g_object_unref(G_OBJECT(model)); + gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(dialog->scale_treeview)); + + dialog->button_scale_del = GTK_BUTTON(glade_xml_get_widget(_gladeXML, + "button_scale_del")); + dialog->button_scale_add = GTK_BUTTON(glade_xml_get_widget(_gladeXML, + "button_scale_add")); + GtkButton* button_import = GTK_BUTTON(glade_xml_get_widget(_gladeXML, + "button_scale_import")); + + g_signal_connect(G_OBJECT(dialog->scale_treeview), "key_press_event", + G_CALLBACK(lingot_gui_config_dialog_scale_key_press_cb), dialog); + + g_signal_connect(G_OBJECT(dialog->button_scale_add), "clicked", G_CALLBACK( + lingot_gui_config_dialog_scale_tree_add_row_tree), + dialog->scale_treeview); + g_signal_connect(G_OBJECT(dialog->button_scale_del), "clicked", G_CALLBACK( + lingot_gui_config_dialog_scale_tree_remove_selected_items), + dialog->scale_treeview); + g_signal_connect(G_OBJECT(button_import), "clicked", G_CALLBACK( + lingot_gui_config_dialog_import_scl), dialog); + + gtk_widget_set_has_tooltip(GTK_WIDGET(dialog->scale_treeview), TRUE); + g_signal_connect(G_OBJECT(dialog->scale_treeview), "query-tooltip", + (GCallback) lingot_gui_config_dialog_scale_table_query_tooltip, + NULL); +} diff --git a/src/lingot-gui-config-dialog-scale.h b/src/lingot-gui-config-dialog-scale.h new file mode 100644 index 0000000..5b7b522 --- /dev/null +++ b/src/lingot-gui-config-dialog-scale.h @@ -0,0 +1,47 @@ +/* + * 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 + */ + +#ifndef LINGOT_GUI_CONFIG_DIALOG_SCALE_H_ +#define LINGOT_GUI_CONFIG_DIALOG_SCALE_H_ + +#include <gtk/gtk.h> + +struct LingotConfigDialog; +struct LingotScale; +struct GladeXML; + +// initialize and show the components +void lingot_gui_config_dialog_scale_show(LingotConfigDialog*, GladeXML*); + +// validate the information stored in the table +int lingot_gui_config_dialog_scale_validate(LingotConfigDialog* dialog, + LingotScale* scale); + +// copies the information stores in the table to the internal data structure +void lingot_gui_config_dialog_scale_apply(LingotConfigDialog* dialog, + LingotScale* scale); + +// fills the table with the information carried by the structure +void lingot_gui_config_dialog_scale_rewrite(LingotConfigDialog* dialog, + LingotScale* scale); + +#endif /* LINGOT_GUI_CONFIG_DIALOG_SCALE_H_ */ diff --git a/src/lingot-gui-config-dialog.c b/src/lingot-gui-config-dialog.c new file mode 100644 index 0000000..ef76889 --- /dev/null +++ b/src/lingot-gui-config-dialog.c @@ -0,0 +1,435 @@ +/* + * 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 <stdio.h> +#include <math.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <glade/glade.h> + +#include "lingot-defs.h" + +#include "lingot-core.h" +#include "lingot-config.h" +#include "lingot-gui-mainframe.h" +#include "lingot-gui-config-dialog.h" +#include "lingot-i18n.h" +#include "lingot-config.h" +#include "lingot-gui-config-dialog-scale.h" +#include "lingot-msg.h" + +int lingot_gui_config_dialog_apply(LingotConfigDialog*); +void lingot_gui_config_dialog_close(LingotConfigDialog*); +void lingot_gui_config_dialog_rewrite(LingotConfigDialog*); +void lingot_gui_config_dialog_combo_select_value(GtkWidget* combo, int value); +audio_system_t lingot_gui_config_dialog_get_audio_system(GtkComboBox* combo); + +/* button press event attention routine. */ + +void lingot_gui_config_dialog_callback_button_cancel(GtkButton *button, + LingotConfigDialog* dialog) { + lingot_gui_config_dialog_close(dialog); +} + +void lingot_gui_config_dialog_callback_button_ok(GtkButton *button, + LingotConfigDialog* dialog) { + if (lingot_gui_config_dialog_apply(dialog)) { + // dumps the current config to the config file + lingot_config_save(dialog->conf, CONFIG_FILE_NAME); + // establish the current config as the old config, so the close rollback + // will do nothing. + lingot_config_copy(dialog->conf_old, dialog->conf); + lingot_gui_config_dialog_close(dialog); + } +} + +void lingot_gui_config_dialog_callback_button_apply(GtkButton *button, + LingotConfigDialog* dialog) { + if (lingot_gui_config_dialog_apply(dialog)) { + lingot_gui_config_dialog_rewrite(dialog); + } +} + +void lingot_gui_config_dialog_callback_button_default(GtkButton *button, + LingotConfigDialog* dialog) { + lingot_config_restore_default_values(dialog->conf); + lingot_gui_config_dialog_rewrite(dialog); +} + +void lingot_gui_config_dialog_callback_cancel(GtkWidget *widget, + LingotConfigDialog* dialog) { + //lingot_mainframe_change_config(dialog->mainframe, dialog->conf_old); // restore old configuration. + lingot_gui_config_dialog_close(dialog); +} + +void lingot_gui_config_dialog_callback_close(GtkWidget *widget, + LingotConfigDialog *dialog) { + lingot_gui_mainframe_change_config(dialog->mainframe, dialog->conf_old); // restore old configuration. + gtk_widget_destroy(dialog->win); + lingot_gui_config_dialog_destroy(dialog); +} + +void lingot_gui_config_dialog_callback_change_sample_rate(GtkWidget *widget, + LingotConfigDialog *dialog) { + + const gchar* text = gtk_entry_get_text(GTK_ENTRY( + GTK_BIN(dialog->sample_rate)->child)); + + int sr; + if (text != NULL) { + sr = atoi(text); + } else { + sr = 44100; + //g_print("WARNING: cannot get sample rate, assuming 44100\n"); + } + + char buff1[20]; + char buff2[20]; + gdouble srf = sr; + gdouble os = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON( + dialog->oversampling)); + sprintf(buff1, "%d", sr); + sprintf(buff2, " = %0.1f", srf / os); + gtk_label_set_text(dialog->label_sample_rate1, buff1); + //gtk_label_set_text(dialog->jack_label_sample_rate1, buff1); + gtk_label_set_text(dialog->label_sample_rate2, buff2); +} + +void lingot_gui_config_dialog_callback_change_input_system(GtkWidget *widget, + LingotConfigDialog *dialog) { + + char buff[10]; + char* text = gtk_combo_box_get_active_text(dialog->input_system); + audio_system_t audio_system = str_to_audio_system_t(text); + free(text); + + LingotAudioSystemProperties* properties = + lingot_audio_get_audio_system_properties(audio_system); + + if (properties != NULL) { + if ((properties->forced_sample_rate) + && (properties->n_sample_rates > 0)) { + sprintf(buff, "%d", properties->sample_rates[0]); + gtk_entry_set_text(GTK_ENTRY(GTK_BIN(dialog->sample_rate)->child), + buff); + } + + gtk_widget_set_sensitive(GTK_WIDGET(dialog->sample_rate), + (gboolean) !properties->forced_sample_rate); + + GtkListStore* model = GTK_LIST_STORE(gtk_combo_box_get_model( + GTK_COMBO_BOX(dialog->sample_rate))); + gtk_list_store_clear(model); + + int i; + for (i = 0; i < properties->n_sample_rates; i++) { + sprintf(buff, "%d", properties->sample_rates[i]); + gtk_combo_box_append_text(GTK_COMBO_BOX(dialog->sample_rate), buff); + } + + // TODO: devices + + gtk_entry_set_text(GTK_ENTRY(GTK_BIN(dialog->input_dev)->child), + dialog->conf->audio_dev[audio_system]); + gtk_widget_set_sensitive(GTK_WIDGET(dialog->input_dev), + (gboolean) (audio_system != AUDIO_SYSTEM_JACK)); + + lingot_audio_audio_system_properties_destroy(properties); + } else { + gtk_entry_set_text(GTK_ENTRY(GTK_BIN(dialog->input_dev)->child), ""); + gtk_widget_set_sensitive(GTK_WIDGET(dialog->input_dev), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(dialog->sample_rate), FALSE); + } +} + +void lingot_gui_config_dialog_callback_change_deviation(GtkWidget *widget, + LingotConfigDialog *dialog) { + gtk_widget_queue_draw(GTK_WIDGET(dialog->scale_treeview)); +} + +void lingot_gui_config_dialog_set_audio_system(GtkComboBox* combo, + audio_system_t audio_system) { + const char* token = audio_system_t_to_str(audio_system); + GtkTreeModel* model = gtk_combo_box_get_model(combo); + GtkTreeIter iter; + + gboolean valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid) { + gchar *str_data; + gtk_tree_model_get(model, &iter, 0, &str_data, -1); + if (!strcmp(str_data, token)) + gtk_combo_box_set_active_iter(combo, &iter); + g_free(str_data); + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +audio_system_t lingot_gui_config_dialog_get_audio_system(GtkComboBox* combo) { + char* text = gtk_combo_box_get_active_text(combo); + int result = str_to_audio_system_t(text); + free(text); + return result; +} + +void lingot_gui_config_dialog_combo_select_value(GtkWidget* combo, int value) { + + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + GtkTreeIter iter; + + gboolean valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid) { + gchar *str_data; + gtk_tree_model_get(model, &iter, 0, &str_data, -1); + if (atoi(str_data) == value) + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); + g_free(str_data); + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +void lingot_gui_config_dialog_rewrite(LingotConfigDialog* dialog) { + LingotConfig* conf = dialog->conf; + + lingot_gui_config_dialog_set_audio_system(dialog->input_system, + conf->audio_system); + // gtk_entry_set_text(dialog->oss_input_dev, conf->audio_dev); + // gtk_entry_set_text(dialog->alsa_input_dev, conf->audio_dev_alsa); + gtk_entry_set_text(GTK_ENTRY(GTK_BIN(dialog->input_dev)->child), + conf->audio_dev[conf->audio_system]); + + gtk_range_set_value(GTK_RANGE(dialog->calculation_rate), + conf->calculation_rate); + gtk_range_set_value(GTK_RANGE(dialog->visualization_rate), + conf->visualization_rate); + gtk_range_set_value(GTK_RANGE(dialog->noise_threshold), + conf->noise_threshold_db); + gtk_range_set_value(GTK_RANGE(dialog->gain), conf->gain); + gtk_spin_button_set_value(dialog->oversampling, conf->oversampling); + // lingot_config_dialog_set_root_reference_note( + // dialog->combo_root_frequency_reference_note, + // conf->root_frequency_referente_note); + gtk_spin_button_set_value(dialog->root_frequency_error, + conf->root_frequency_error); + gtk_spin_button_set_value(dialog->temporal_window, conf->temporal_window); + gtk_spin_button_set_value(dialog->dft_number, conf->dft_number); + gtk_spin_button_set_value(dialog->dft_size, conf->dft_size); + gtk_spin_button_set_value(dialog->peak_number, conf->peak_number); + gtk_spin_button_set_value(dialog->peak_halfwidth, conf->peak_half_width); + gtk_spin_button_set_value(dialog->minimum_frequency, conf->min_frequency); + gtk_range_set_value(GTK_RANGE(dialog->rejection_peak_relation), + conf->peak_rejection_relation_db); + lingot_gui_config_dialog_combo_select_value(GTK_WIDGET(dialog->fft_size), + (int) conf->fft_size); + + char buff[10]; + sprintf(buff, "%d", conf->sample_rate); + gtk_entry_set_text(GTK_ENTRY(GTK_BIN(dialog->sample_rate)->child), buff); + + lingot_gui_config_dialog_scale_rewrite(dialog, conf->scale); +} + +void lingot_gui_config_dialog_destroy(LingotConfigDialog* dialog) { + dialog->mainframe->config_dialog = NULL; + lingot_config_destroy(dialog->conf); + lingot_config_destroy(dialog->conf_old); + free(dialog); +} + +int lingot_gui_config_dialog_apply(LingotConfigDialog* dialog) { + + gchar* text1; + const gchar* text2; + LingotConfig* conf = dialog->conf; + + if (!lingot_gui_config_dialog_scale_validate(dialog, conf->scale)) { + return 0; + } + + conf->audio_system = lingot_gui_config_dialog_get_audio_system( + dialog->input_system); + sprintf(conf->audio_dev[conf->audio_system], "%s", gtk_entry_get_text( + GTK_ENTRY(GTK_BIN(dialog->input_dev)->child))); + conf->root_frequency_error = gtk_spin_button_get_value_as_float( + dialog->root_frequency_error); + conf->calculation_rate = gtk_range_get_value(GTK_RANGE( + dialog->calculation_rate)); + conf->visualization_rate = gtk_range_get_value(GTK_RANGE( + dialog->visualization_rate)); + conf->temporal_window = gtk_spin_button_get_value_as_float( + dialog->temporal_window); + conf->noise_threshold_db = gtk_range_get_value(GTK_RANGE( + dialog->noise_threshold)); + conf->gain = gtk_range_get_value(GTK_RANGE(dialog->gain)); + conf->oversampling = gtk_spin_button_get_value_as_int(dialog->oversampling); + conf->dft_number = gtk_spin_button_get_value_as_int(dialog->dft_number); + conf->dft_size = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON( + dialog->dft_size)); + conf->peak_number = gtk_spin_button_get_value_as_int(dialog->peak_number); + conf->peak_half_width = gtk_spin_button_get_value_as_int( + dialog->peak_halfwidth); + conf->peak_rejection_relation_db = gtk_range_get_value(GTK_RANGE( + dialog->rejection_peak_relation)); + conf->min_frequency = gtk_spin_button_get_value_as_int( + dialog->minimum_frequency); + text1 = gtk_combo_box_get_active_text(dialog->fft_size); + conf->fft_size = atoi(text1); + g_free(text1); + text2 = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(dialog->sample_rate)->child)); + conf->sample_rate = atoi(text2); + + LingotScale* scale = conf->scale; + lingot_config_scale_destroy(scale); + + lingot_gui_config_dialog_scale_apply(dialog, scale); + + lingot_config_update_internal_params(conf); + lingot_gui_mainframe_change_config(dialog->mainframe, conf); + + if (scale->max_offset_rounded > 200) { + lingot_msg_add_warning( + _("The provided scale contains wide gaps in frequency that increase the gauge range and produce a loss of visual accuracy. Consider providing scales with at least 12 tones, or with a maximum distance between adjacent notes below 200 cents.")); + } + + return 1; +} + +void lingot_gui_config_dialog_close(LingotConfigDialog* dialog) { + dialog->mainframe->config_dialog = NULL; + gtk_widget_destroy(dialog->win); +} + +void lingot_gui_config_dialog_show(LingotMainFrame* frame, LingotConfig* config) { + GladeXML* _gladeXML = NULL; + + if (frame->config_dialog == NULL) { + + LingotConfigDialog* dialog = malloc(sizeof(LingotConfigDialog)); + + dialog->mainframe = frame; + dialog->conf = lingot_config_new(); + dialog->conf_old = lingot_config_new(); + + // config copy + lingot_config_copy(dialog->conf, (config == NULL) ? frame->conf + : config); + lingot_config_copy(dialog->conf_old, frame->conf); + + // TODO: obtain glade files installation dir by other way +# define FILE_NAME "lingot-config-dialog.glade" + FILE* fd = fopen("src/glade/" FILE_NAME, "r"); + if (fd != NULL) { + fclose(fd); + _gladeXML = glade_xml_new("src/glade/" FILE_NAME, "dialog1", NULL); + } else { + _gladeXML = glade_xml_new(LINGOT_GLADEDIR FILE_NAME, "dialog1", + NULL); + } +# undef FILE_NAME + + dialog->win = glade_xml_get_widget(_gladeXML, "dialog1"); + + gtk_window_set_icon(GTK_WINDOW(dialog->win), gtk_window_get_icon( + GTK_WINDOW(frame->win))); + //gtk_window_set_position(GTK_WINDOW(dialog->win), GTK_WIN_POS_CENTER_ALWAYS); + dialog->mainframe->config_dialog = dialog; + + dialog->input_system + = GTK_COMBO_BOX(glade_xml_get_widget(_gladeXML, "input_system")); + dialog->input_dev + = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(_gladeXML, "input_dev")); + dialog->sample_rate + = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(_gladeXML, "sample_rate")); + dialog->calculation_rate + = GTK_HSCALE(glade_xml_get_widget(_gladeXML, "calculation_rate")); + dialog->visualization_rate + = GTK_HSCALE(glade_xml_get_widget(_gladeXML, "visualization_rate")); + dialog->noise_threshold + = GTK_HSCALE(glade_xml_get_widget(_gladeXML, "noise_threshold")); + dialog->gain = GTK_HSCALE(glade_xml_get_widget(_gladeXML, "gain")); + dialog->oversampling + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, "oversampling")); + dialog->fft_size + = GTK_COMBO_BOX(glade_xml_get_widget(_gladeXML, "fft_size")); + dialog->temporal_window + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, "temporal_window")); + dialog->root_frequency_error + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, + "root_frequency_error")); + dialog->dft_number + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, "dft_number")); + dialog->dft_size + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, "dft_size")); + dialog->peak_number + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, "peak_number")); + dialog->peak_halfwidth + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, "peak_halfwidth")); + dialog->rejection_peak_relation + = GTK_HSCALE(glade_xml_get_widget(_gladeXML, + "rejection_peak_relation")); + dialog->label_sample_rate1 = GTK_LABEL(glade_xml_get_widget(_gladeXML, + "label_sample_rate1")); + dialog->label_sample_rate2 = GTK_LABEL(glade_xml_get_widget(_gladeXML, + "label_sample_rate2")); + dialog->minimum_frequency + = GTK_SPIN_BUTTON(glade_xml_get_widget(_gladeXML, + "minimum_frequency")); + + lingot_gui_config_dialog_scale_show(dialog, _gladeXML); + + gtk_signal_connect(GTK_OBJECT(dialog->input_system), "changed", + GTK_SIGNAL_FUNC (lingot_gui_config_dialog_callback_change_input_system), dialog); + gtk_signal_connect(GTK_OBJECT(GTK_BIN(dialog->sample_rate)->child), "changed", + GTK_SIGNAL_FUNC (lingot_gui_config_dialog_callback_change_sample_rate), dialog); + + gtk_signal_connect (GTK_OBJECT (dialog->oversampling), "value_changed", + GTK_SIGNAL_FUNC (lingot_gui_config_dialog_callback_change_sample_rate), dialog); + gtk_signal_connect (GTK_OBJECT (dialog->root_frequency_error), "value_changed", + GTK_SIGNAL_FUNC (lingot_gui_config_dialog_callback_change_deviation), dialog); + + g_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "button_default")), "clicked", GTK_SIGNAL_FUNC(lingot_gui_config_dialog_callback_button_default), dialog); + g_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "button_apply")), "clicked", GTK_SIGNAL_FUNC(lingot_gui_config_dialog_callback_button_apply), dialog); + g_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "button_accept")), "clicked", GTK_SIGNAL_FUNC(lingot_gui_config_dialog_callback_button_ok), dialog); + g_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "button_cancel")), "clicked", GTK_SIGNAL_FUNC(lingot_gui_config_dialog_callback_button_cancel), dialog); + g_signal_connect(GTK_OBJECT(dialog->win), "destroy", GTK_SIGNAL_FUNC(lingot_gui_config_dialog_callback_close), dialog); + + lingot_gui_config_dialog_rewrite(dialog); + + gtk_widget_show(dialog->win); + GtkWidget* scroll = glade_xml_get_widget(_gladeXML, "scrolledwindow1"); + gtk_widget_show_all(scroll); + g_object_unref(_gladeXML); + } else { + if (config != NULL) { + lingot_config_copy(frame->config_dialog->conf, config); + lingot_gui_config_dialog_rewrite(frame->config_dialog); + } + + gtk_window_present(GTK_WINDOW(frame->config_dialog->win)); + } + + if (config != NULL) { + lingot_config_destroy(config); + } +} diff --git a/src/lingot-gui-config-dialog.h b/src/lingot-gui-config-dialog.h new file mode 100644 index 0000000..0fcb98c --- /dev/null +++ b/src/lingot-gui-config-dialog.h @@ -0,0 +1,73 @@ +/* + * 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 + */ + +#ifndef __LINGOT_GUI_CONFIG_DIALOG_H__ +#define __LINGOT_GUI_CONFIG_DIALOG_H__ + +#include <gtk/gtk.h> + +#include "lingot-config.h" + +typedef struct _LingotConfigDialog LingotConfigDialog; + +struct _LingotConfigDialog { + + // widgets that contains configuration information. + GtkComboBox* input_system; + GtkComboBoxEntry* input_dev; + GtkComboBoxEntry* sample_rate; + GtkHScale* calculation_rate; + GtkHScale* visualization_rate; + GtkHScale* noise_threshold; + GtkHScale* gain; + GtkSpinButton* oversampling; + GtkComboBox* fft_size; + GtkSpinButton* temporal_window; + GtkSpinButton* root_frequency_error; + GtkSpinButton* dft_number; + GtkSpinButton* dft_size; + GtkSpinButton* peak_number; + GtkSpinButton* peak_halfwidth; + GtkSpinButton* minimum_frequency; + GtkHScale* rejection_peak_relation; + GtkLabel* label_sample_rate1; + GtkLabel* label_sample_rate2; + + GtkButton* button_scale_add; + GtkButton* button_scale_del; + + GtkEntry* scale_name; + GtkTreeView* scale_treeview; + + LingotConfig* conf; // provisional configuration. + LingotConfig* conf_old; // restoration point for cancel. + + LingotMainFrame* mainframe; + + GtkWidget* win; // window +}; + +//LingotConfigDialog* lingot_config_dialog_new(LingotMainFrame*); +void lingot_gui_config_dialog_destroy(LingotConfigDialog*); +void lingot_gui_config_dialog_show(LingotMainFrame* frame, LingotConfig* config); + +#endif // __LINGOT_GUI_CONFIG_DIALOG_H__ diff --git a/src/lingot-gui-mainframe.c b/src/lingot-gui-mainframe.c new file mode 100644 index 0000000..30c1b69 --- /dev/null +++ b/src/lingot-gui-mainframe.c @@ -0,0 +1,861 @@ +/* + * 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 <stdio.h> +#include <math.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <glade/glade.h> + +#include "lingot-defs.h" + +#include "lingot-config.h" +#include "lingot-gui-mainframe.h" +#include "lingot-gui-config-dialog.h" +#include "lingot-gauge.h" +#include "lingot-i18n.h" + +#include "lingot-msg.h" + +#include "lingot-background.xpm" +#include "lingot-logo.xpm" + +void lingot_gui_mainframe_redraw(LingotMainFrame*); +void lingot_mainframe_filter_frequency_value(LingotMainFrame*); +void lingot_gui_mainframe_draw_gauge(LingotMainFrame*); +void lingot_gui_mainframe_draw_spectrum_and_labels(LingotMainFrame*); + +GdkColor black_color; +GdkColor cents_color; +GdkColor gauge_color; +GdkColor spectrum_background_color; +GdkColor spectrum_color; +GdkColor noise_threshold_color; +GdkColor grid_color; +GdkColor freq_color; + +// sizes + +int gauge_size_x = 160; +int gauge_size_y = 100; + +int spectrum_size_y = 64; + +// spectrum area margins +int spectrum_bottom_margin = 16; +int spectrum_top_margin = 12; +int spectrum_x_margin = 15; + +PangoFontDescription* spectrum_legend_font_desc; +PangoFontDescription* gauge_cents_font_desc; + +gchar* filechooser_config_last_folder = NULL; + +void lingot_gui_mainframe_callback_redraw(GtkWidget* w, GdkEventExpose* e, + LingotMainFrame* frame) { + lingot_gui_mainframe_redraw(frame); +} + +void lingot_gui_mainframe_callback_destroy(GtkWidget* w, LingotMainFrame* frame) { + g_source_remove(frame->visualization_timer_uid); + g_source_remove(frame->freq_computation_timer_uid); + g_source_remove(frame->gauge_computation_uid); + gtk_main_quit(); +} + +void lingot_gui_mainframe_callback_about(GtkWidget* w, LingotMainFrame* frame) { + static const gchar* authors[] = { "Ibán Cereijo Graña <ibancg@gmail.com>", + "Jairo Chapela Martínez <jairochapela@gmail.com>", NULL }; + + static const gchar* artists[] = { "Matthew Blissett (Logo design)", NULL }; + + gtk_show_about_dialog( + NULL, + "name", + "Lingot", + "version", + VERSION, + "copyright", + "\xC2\xA9 2004-2011 Ibán Cereijo Graña\n\xC2\xA9 2004-2011 Jairo Chapela Martínez", + "comments", _("Accurate and easy to use musical instrument tuner"), + "authors", authors, "artists", artists, "website-label", + "http://lingot.nongnu.org/", "website", + "http://lingot.nongnu.org/", "translator-credits", + _("translator-credits"), "logo-icon-name", "lingot-logo", + NULL); +} + +void lingot_gui_mainframe_callback_view_spectrum(GtkWidget* w, + LingotMainFrame* frame) { + gtk_widget_set_visible(frame->spectrum_frame, + gtk_check_menu_item_get_active( + GTK_CHECK_MENU_ITEM(frame->view_spectrum_item))); +} + +void lingot_gui_mainframe_callback_config_dialog(GtkWidget* w, + LingotMainFrame* frame) { + lingot_gui_config_dialog_show(frame, NULL); +} + +unsigned short int lingot_gui_mainframe_get_closest_note_index(FLT freq, + LingotScale* scale, FLT deviation, FLT* error_cents) { + unsigned short note_index = 0; + unsigned short int index; + + FLT offset = 1200.0 * log2(freq / scale->base_frequency) - deviation; + offset = fmod(offset, 1200.0); + if (offset < 0.0) { + offset += 1200.0; + } + + index = floor(scale->notes * offset / 1200.0); + + FLT pitch_inf; + FLT pitch_sup; + int n = 0; + for (;;) { + n++; + pitch_inf = scale->offset_cents[index]; + pitch_sup = ((index + 1) < scale->notes) ? scale->offset_cents[index + + 1] : 1200.0; + + if (offset > pitch_sup) { + index++; + continue; + } + + if (offset < pitch_inf) { + index--; + continue; + } + + break; + }; + + if (fabs(offset - pitch_inf) < fabs(offset - pitch_sup)) { + note_index = index; + *error_cents = offset - pitch_inf; + } else { + note_index = index + 1; + *error_cents = offset - pitch_sup; + } + + return note_index; +} + +/* timeout for gauge and labels visualization */ +gboolean lingot_gui_mainframe_callback_tout_visualization(gpointer data) { + unsigned int period; + + LingotMainFrame* frame = (LingotMainFrame*) data; + + period = 1000 / frame->conf->visualization_rate; + frame->visualization_timer_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_tout_visualization, frame); + + lingot_gui_mainframe_draw_gauge(frame); + + return 0; +} + +/* timeout for spectrum computation and display */ +gboolean lingot_gui_mainframe_callback_tout_spectrum_computation_display( + gpointer data) { + unsigned int period; + + LingotMainFrame* frame = (LingotMainFrame*) data; + + period = 1000 / frame->conf->calculation_rate; + frame->freq_computation_timer_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_tout_spectrum_computation_display, + frame); + + lingot_gui_mainframe_draw_spectrum_and_labels(frame); + //lingot_core_compute_fundamental_fequency(frame->core); + + return 0; +} + +/* timeout for a new gauge position computation */ +gboolean lingot_gui_mainframe_callback_gauge_computation(gpointer data) { + unsigned int period; + double error_cents; + LingotMainFrame* frame = (LingotMainFrame*) data; + unsigned short note_index; + + period = 1000 / GAUGE_RATE; + frame->gauge_computation_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_gauge_computation, frame); + + if (!frame->core->running || isnan(frame->core->freq) || (frame->core->freq + < 10.0)) { + lingot_gauge_compute(frame->gauge, frame->conf->gauge_rest_value); + } else { + note_index = lingot_gui_mainframe_get_closest_note_index( + frame->core->freq, frame->conf->scale, + frame->conf->root_frequency_error, &error_cents); + lingot_gauge_compute(frame->gauge, error_cents); + } + + return 0; +} + +/* timeout for dispatching the error queue */ +gboolean lingot_gui_mainframe_callback_error_dispatcher(gpointer data) { + unsigned int period; + GtkWidget* message_dialog; + LingotMainFrame* frame = (LingotMainFrame*) data; + + char* error_message = NULL; + message_type_t message_type; + int more_messages; + + do { + more_messages = lingot_msg_get(&error_message, &message_type); + + if (more_messages) { + message_dialog + = gtk_message_dialog_new( + GTK_WINDOW((frame->config_dialog + != NULL) ? frame->config_dialog->win : frame->win), + GTK_DIALOG_DESTROY_WITH_PARENT, + (message_type == ERROR) ? GTK_MESSAGE_ERROR + : ((message_type == WARNING) ? GTK_MESSAGE_WARNING + : GTK_MESSAGE_INFO), + GTK_BUTTONS_CLOSE, error_message); + gtk_window_set_title(GTK_WINDOW(message_dialog), (message_type + == ERROR) ? _("Error") + : ((message_type == WARNING) ? _("Warning") : _("Info"))); + gtk_window_set_icon(GTK_WINDOW(message_dialog), + gtk_window_get_icon(GTK_WINDOW(frame->win))); + gtk_dialog_run(GTK_DIALOG(message_dialog)); + gtk_widget_destroy(message_dialog); + free(error_message); + } + } while (more_messages); + + period = 1000 / ERROR_DISPATCH_RATE; + frame->error_dispatcher_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_error_dispatcher, frame); + + return 0; +} + +void lingot_gui_mainframe_callback_open_config(gpointer data, + LingotMainFrame* frame) { + GtkWidget * dialog = gtk_file_chooser_dialog_new( + _("Open Configuration File"), GTK_WINDOW(frame->win), + GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + GtkFileFilter *filefilter; + LingotConfig* config = NULL; + filefilter = gtk_file_filter_new(); + + gtk_file_filter_set_name(filefilter, + (const gchar *) _("Lingot configuration files")); + gtk_file_filter_add_pattern(filefilter, "*.conf"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filefilter); + gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE); + + if (filechooser_config_last_folder != NULL) { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), + filechooser_config_last_folder); + } + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + if (filechooser_config_last_folder != NULL) + free(filechooser_config_last_folder); + filechooser_config_last_folder = strdup( + gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog))); + config = lingot_config_new(); + lingot_config_load(config, filename); + g_free(filename); + } + gtk_widget_destroy(dialog); + //g_free(filefilter); + + if (config != NULL) { + lingot_gui_config_dialog_show(frame, config); + } +} + +void lingot_gui_mainframe_callback_save_config(gpointer data, + LingotMainFrame* frame) { + GtkWidget *dialog = gtk_file_chooser_dialog_new( + _("Save Configuration File"), GTK_WINDOW(frame->win), + GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (dialog), + TRUE); + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER (dialog), + "untitled.conf"); + GtkFileFilter* filefilter = gtk_file_filter_new(); + + gtk_file_filter_set_name(filefilter, + (const gchar *) _("Lingot configuration files")); + gtk_file_filter_add_pattern(filefilter, "*.conf"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filefilter); + gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE); + + if (filechooser_config_last_folder != NULL) { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), + filechooser_config_last_folder); + } + + if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog)); + if (filechooser_config_last_folder != NULL) + free(filechooser_config_last_folder); + filechooser_config_last_folder = strdup( + gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog))); + lingot_config_save(frame->conf, filename); + g_free(filename); + } + gtk_widget_destroy(dialog); +} + +void lingot_gui_mainframe_color(GdkColor* color, int red, int green, int blue) { + color->red = red; + color->green = green; + color->blue = blue; +} + +void lingot_gui_mainframe_create(int argc, char *argv[]) { + + LingotMainFrame* frame; + LingotConfig* conf; + GladeXML* _gladeXML = NULL; + + if (filechooser_config_last_folder == NULL) { + char buff[1000]; + sprintf(buff, "%s/%s", getenv("HOME"), CONFIG_DIR_NAME); + filechooser_config_last_folder = strdup(buff); + } + + frame = malloc(sizeof(LingotMainFrame)); + + frame->config_dialog = NULL; + frame->pix_stick = NULL; + + frame->conf = lingot_config_new(); + conf = frame->conf; + lingot_config_load(conf, CONFIG_FILE_NAME); + + frame->gauge = lingot_gauge_new(conf->gauge_rest_value); // gauge in rest situation + + // ----- FREQUENCY FILTER CONFIGURATION ------ + + // low pass IIR filter. + FLT freq_filter_a[] = { 1.0, -0.5 }; + FLT freq_filter_b[] = { 0.5 }; + + frame->freq_filter = lingot_filter_new(1, 0, freq_filter_a, freq_filter_b); + + // --------------------------------------------------- + + gtk_init(&argc, &argv); + gtk_set_locale(); + +# define FILE_NAME "lingot-mainframe.glade" + FILE* fd = fopen("src/glade/" FILE_NAME, "r"); + if (fd != NULL) { + fclose(fd); + _gladeXML = glade_xml_new("src/glade/" FILE_NAME, "window1", NULL); + } else { + _gladeXML = glade_xml_new(LINGOT_GLADEDIR FILE_NAME, "window1", NULL); + } +# undef FILE_NAME + + frame->win = glade_xml_get_widget(_gladeXML, "window1"); + + GdkPixbuf* logo = gdk_pixbuf_new_from_xpm_data(lingotlogo); + gtk_icon_theme_add_builtin_icon("lingot-logo", 64, logo); + + gtk_window_set_icon(GTK_WINDOW(frame->win), logo); + + frame->gauge_area = glade_xml_get_widget(_gladeXML, "gauge_area"); + frame->spectrum_area = glade_xml_get_widget(_gladeXML, "spectrum_area"); + + frame->freq_label = glade_xml_get_widget(_gladeXML, "freq_label"); + frame->tone_label = glade_xml_get_widget(_gladeXML, "tone_label"); + frame->error_label = glade_xml_get_widget(_gladeXML, "error_label"); + + frame->spectrum_frame = glade_xml_get_widget(_gladeXML, "spectrum_frame"); + frame->spectrum_scroll + = GTK_SCROLLED_WINDOW(glade_xml_get_widget(_gladeXML, "scrolledwindow1")); + frame->view_spectrum_item + = glade_xml_get_widget(_gladeXML, "spectrum_item"); + + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(frame->view_spectrum_item), TRUE); + + // show all + gtk_widget_show_all(frame->win); + + int x = ((conf->fft_size > 256) ? (conf->fft_size >> 1) : 256) + 2 + * spectrum_x_margin; + int y = spectrum_size_y + spectrum_bottom_margin + spectrum_top_margin; + + // two pixmaps for double buffer in gauge and spectrum drawing + // (virtual screen) + gdk_pixmap_new(frame->gauge_area->window, gauge_size_x, gauge_size_y, -1); + frame->pix_spectrum + = gdk_pixmap_new(frame->spectrum_area->window, x, y, -1); + + // GTK signals + gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "preferences_item")), "activate", + GTK_SIGNAL_FUNC(lingot_gui_mainframe_callback_config_dialog), frame); + gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "quit_item")), "activate", GTK_SIGNAL_FUNC( + lingot_gui_mainframe_callback_destroy), frame); + gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "about_item")), "activate", GTK_SIGNAL_FUNC( + lingot_gui_mainframe_callback_about), frame); + gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "spectrum_item")), "activate", + GTK_SIGNAL_FUNC(lingot_gui_mainframe_callback_view_spectrum), frame); + gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "open_config_item")), "activate", + GTK_SIGNAL_FUNC(lingot_gui_mainframe_callback_open_config), frame); + gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(_gladeXML, "save_config_item")), "activate", + GTK_SIGNAL_FUNC(lingot_gui_mainframe_callback_save_config), frame); + + gtk_signal_connect(GTK_OBJECT(frame->gauge_area), "expose_event", + GTK_SIGNAL_FUNC(lingot_gui_mainframe_callback_redraw), frame); + gtk_signal_connect(GTK_OBJECT(frame->spectrum_area), "expose_event", + GTK_SIGNAL_FUNC(lingot_gui_mainframe_callback_redraw), frame); + gtk_signal_connect(GTK_OBJECT(frame->win), "destroy", GTK_SIGNAL_FUNC( + lingot_gui_mainframe_callback_destroy), frame); + + GtkAccelGroup* accel_group = gtk_accel_group_new(); + gtk_widget_add_accelerator(glade_xml_get_widget(_gladeXML, + "preferences_item"), "activate", accel_group, 'p', + GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); + gtk_window_add_accel_group(GTK_WINDOW(frame->win), accel_group); + + unsigned int period; + period = 1000 / conf->visualization_rate; + frame->visualization_timer_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_tout_visualization, frame); + + period = 1000 / conf->calculation_rate; + frame->freq_computation_timer_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_tout_spectrum_computation_display, + frame); + + period = 1000 / GAUGE_RATE; + frame->gauge_computation_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_gauge_computation, frame); + + period = 1000 / ERROR_DISPATCH_RATE; + frame->error_dispatcher_uid = g_timeout_add(period, + lingot_gui_mainframe_callback_error_dispatcher, frame); + + lingot_gui_mainframe_color(&gauge_color, 0xC000, 0x0000, 0x2000); + lingot_gui_mainframe_color(&spectrum_background_color, 0x1111, 0x3333, + 0x1111); + lingot_gui_mainframe_color(&spectrum_color, 0x2222, 0xEEEE, 0x2222); + lingot_gui_mainframe_color(&noise_threshold_color, 0x8888, 0x8888, 0x2222); + lingot_gui_mainframe_color(&grid_color, 0x9000, 0x9000, 0x9000); + lingot_gui_mainframe_color(&freq_color, 0xFFFF, 0x2222, 0x2222); + lingot_gui_mainframe_color(¢s_color, 0x3000, 0x3000, 0x3000); + + gdk_color_alloc(gdk_colormap_get_system(), &gauge_color); + gdk_color_alloc(gdk_colormap_get_system(), &spectrum_color); + gdk_color_alloc(gdk_colormap_get_system(), &spectrum_background_color); + gdk_color_alloc(gdk_colormap_get_system(), &noise_threshold_color); + gdk_color_alloc(gdk_colormap_get_system(), &grid_color); + gdk_color_alloc(gdk_colormap_get_system(), &freq_color); + gdk_color_black(gdk_colormap_get_system(), &black_color); + gdk_color_alloc(gdk_colormap_get_system(), ¢s_color); + + spectrum_legend_font_desc = pango_font_description_from_string( + "Helvetica Plain 7"); + gauge_cents_font_desc = pango_font_description_from_string( + "Helvetica Plain 7"); + + frame->core = lingot_core_new(conf); + lingot_core_start(frame->core); + + g_object_unref(_gladeXML); + + gtk_main(); +} + +void lingot_gui_mainframe_destroy(LingotMainFrame* frame) { + + lingot_core_stop(frame->core); + lingot_core_destroy(frame->core); + + lingot_gauge_destroy(frame->gauge); + lingot_filter_destroy(frame->freq_filter); + lingot_config_destroy(frame->conf); + if (frame->config_dialog) + lingot_gui_config_dialog_destroy(frame->config_dialog); + + free(frame); +} + +// --------------------------------------------------------------------------- + +void lingot_gui_mainframe_redraw(LingotMainFrame* frame) { + lingot_gui_mainframe_draw_gauge(frame); + lingot_gui_mainframe_draw_spectrum_and_labels(frame); +} + +// --------------------------------------------------------------------------- + +void lingot_gui_mainframe_draw_gauge(LingotMainFrame* frame) { + GdkGC* gc = frame->gauge_area->style->fg_gc[frame->gauge_area->state]; + GdkWindow* w = frame->gauge_area->window; + GdkGCValues gv; + + gdk_gc_get_values(gc, &gv); + + static FLT gauge_size = 90.0; + FLT max = 1.0; + + // draws background + if (!frame->pix_stick) { + frame->pix_stick = gdk_pixmap_create_from_xpm_d(w, NULL, NULL, + background2_xpm); + } + gdk_draw_pixmap(w, gc, frame->pix_stick, 0, 0, 0, 0, 160, 100); + + // and draws gauge + gdk_gc_set_foreground(gc, &gauge_color); + + FLT normalized_error = frame->gauge->position + / frame->conf->scale->max_offset_rounded; + FLT angle = normalized_error * M_PI / (1.5 * max); + gdk_draw_line(w, gc, gauge_size_x >> 1, gauge_size_y - 1, (gauge_size_x + >> 1) + (int) rint(gauge_size * sin(angle)), gauge_size_y - 1 + - (int) rint(gauge_size * cos(angle))); + + gdk_gc_set_foreground(gc, ¢s_color); + char buff[10]; + sprintf(buff, (frame->conf->scale->max_offset_rounded > 1.0) ? "-%.0f c" + : "-%.1f c", 0.5 * frame->conf->scale->max_offset_rounded); + PangoLayout* layout1 = gtk_widget_create_pango_layout(frame->gauge_area, + buff); + sprintf(buff, (frame->conf->scale->max_offset_rounded > 1.0) ? "+%.0f c" + : "+%.1f c", 0.5 * frame->conf->scale->max_offset_rounded); + PangoLayout* layout2 = gtk_widget_create_pango_layout(frame->gauge_area, + buff); + pango_layout_set_font_description(layout1, gauge_cents_font_desc); + gdk_draw_layout(w, gc, 0.05 * gauge_size_x, 0.8 * gauge_size_y, layout1); + pango_layout_set_font_description(layout2, gauge_cents_font_desc); + gdk_draw_layout(w, gc, 0.75 * gauge_size_x, 0.8 * gauge_size_y, layout2); + g_object_unref(layout1); + g_object_unref(layout2); + + gdk_flush(); +} + +void lingot_gui_mainframe_draw_spectrum_and_labels(LingotMainFrame* frame) { + + char* current_tone; + GtkWidget* widget = NULL; + + static char error_string[30], freq_string[30]; + + PangoLayout* layout; + + int + spectrum_size_x = + ((frame->conf->fft_size > 256) ? (frame->conf->fft_size + >> 1) : 256); + + // minimum grid size in pixels + static int minimum_grid_width = 50; + + // scale factors (in KHz) to draw the grid. We will choose the smaller + // factor that respects the minimum_grid_width + static double scales[] = + { 0.01, 0.05, 0.1, 0.2, 0.5, 1, 2, 4, 11, 22, -1.0 }; + + // spectrum drawing mode + static gboolean spectrum_drawing_filled = TRUE; + + // grid division in dB + static FLT grid_db_height = 25; + + register unsigned int i; + int j; + int old_j; + unsigned short note_index; + + GdkGC * gc = + frame->spectrum_area->style->fg_gc[frame->spectrum_area->state]; + GdkPixmap* pixmap = frame->pix_spectrum; //spectrum->window; + GdkGCValues gv; + gdk_gc_get_values(gc, &gv); + + // clear all + gdk_gc_set_foreground(gc, &spectrum_background_color); + gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, spectrum_size_x + 2 + * spectrum_x_margin, spectrum_size_y + spectrum_bottom_margin + + spectrum_top_margin); + + gdk_gc_set_foreground(gc, &grid_color); + + gdk_draw_line(pixmap, gc, spectrum_x_margin, spectrum_size_y + + spectrum_top_margin, spectrum_x_margin + spectrum_size_x, + spectrum_size_y + spectrum_top_margin); + + // choose scale factor + for (i = 0; scales[i] > 0.0; i++) { + if ((1e3 * scales[i] * frame->conf->fft_size + * frame->conf->oversampling / frame->conf->sample_rate) + > minimum_grid_width) + break; + } + + if (scales[i] < 0.0) + i--; + + FLT scale = scales[i]; + + int grid_width = 1e3 * scales[i] * frame->conf->fft_size + * frame->conf->oversampling / frame->conf->sample_rate; + + char buff[10]; + + FLT freq = 0.0; + for (i = 0; i <= spectrum_size_x; i += grid_width) { + gdk_draw_line(pixmap, gc, spectrum_x_margin + i, spectrum_top_margin, + spectrum_x_margin + i, spectrum_size_y + spectrum_top_margin + + 3); + + if (freq == 0.0) { + sprintf(buff, "0 Hz"); + } else if (floor(freq) == freq) + sprintf(buff, "%0.0f kHz", freq); + else if (floor(10 * freq) == 10 * freq) { + if (freq <= 1000.0) + sprintf(buff, "%0.0f Hz", 1e3 * freq); + else + sprintf(buff, "%0.1f kHz", freq); + } else { + if (freq <= 100.0) + sprintf(buff, "%0.0f Hz", 1e3 * freq); + else + sprintf(buff, "%0.2f kHz", freq); + } + + layout = gtk_widget_create_pango_layout(frame->spectrum_area, buff); + pango_layout_set_font_description(layout, spectrum_legend_font_desc); + gdk_draw_layout(pixmap, gc, spectrum_x_margin - 8 + i, spectrum_size_y + + spectrum_top_margin + 5, layout); + g_object_unref(layout); + freq += scale; + } + +# define PLOT_GAIN 8 + + sprintf(buff, "dB"); + + layout = gtk_widget_create_pango_layout(frame->spectrum_area, buff); + pango_layout_set_font_description(layout, spectrum_legend_font_desc); + gdk_draw_layout(pixmap, gc, spectrum_x_margin - 6, 2, layout); + g_object_unref(layout); + + int grid_height = (int) (PLOT_GAIN + * log10(pow(10.0, grid_db_height / 10.0))); // dB. + j = 0; + for (i = 0; i <= spectrum_size_y; i += grid_height) { + if (j == 0) + sprintf(buff, " %i", j); + else + sprintf(buff, "%i", j); + + layout = gtk_widget_create_pango_layout(frame->spectrum_area, buff); + pango_layout_set_font_description(layout, spectrum_legend_font_desc); + gdk_draw_layout(pixmap, gc, 2, spectrum_size_y + spectrum_top_margin + - i - 5, layout); + g_object_unref(layout); + + gdk_draw_line(pixmap, gc, spectrum_x_margin, spectrum_size_y + + spectrum_top_margin - i, spectrum_x_margin + spectrum_size_x, + spectrum_size_y + spectrum_top_margin - i); + + j += grid_db_height; + } + + gdk_gc_set_foreground(gc, &noise_threshold_color); + + // noise threshold drawing. + j = -1; + for (i = 0; (i < frame->conf->fft_size) && (i < spectrum_size_x); i++) { + if ((i % 10) > 5) + continue; + + FLT noise = frame->conf->noise_threshold_nu; + old_j = j; + j = (noise > 1.0) ? (int) (PLOT_GAIN * log10(noise)) : 0; // dB. + if ((old_j >= 0) && (old_j < spectrum_size_y) && (j >= 0) && (j + < spectrum_size_y)) + gdk_draw_line(pixmap, gc, spectrum_x_margin + i - 1, + spectrum_size_y + spectrum_top_margin - old_j, + spectrum_x_margin + i, spectrum_size_y + + spectrum_top_margin - j); + } + + gdk_gc_set_foreground(gc, &spectrum_color); + + // TODO: change access to frame->core->X + // spectrum drawing. + if (frame->core->running) { + j = -1; + + GtkAdjustment* adj = gtk_scrolled_window_get_hadjustment( + frame->spectrum_scroll); + int min = gtk_adjustment_get_value(adj) - spectrum_x_margin; + int max = min + 256 + 3 * spectrum_x_margin; + if (min < 0) + min = 0; + if (max >= spectrum_size_x) + max = spectrum_size_x; + + if (frame->core->running) { + for (i = min; i < max; i++) { + j = (frame->core->X[i] > 1.0) ? (int) (PLOT_GAIN * log10( + frame->core->X[i])) : 0; // dB. + if (j >= spectrum_size_y) + j = spectrum_size_y - 1; + if (spectrum_drawing_filled) { + gdk_draw_line(pixmap, gc, spectrum_x_margin + i, + spectrum_size_y + spectrum_top_margin - 1, + spectrum_x_margin + i, spectrum_top_margin + + spectrum_size_y - j); + } else { + if ((old_j >= 0) && (old_j < spectrum_size_y) && (j >= 0) + && (j < spectrum_size_y)) + gdk_draw_line(pixmap, gc, spectrum_x_margin + i - 1, + spectrum_size_y + spectrum_top_margin - old_j, + spectrum_x_margin + i, spectrum_size_y + + spectrum_top_margin - j); + old_j = j; + } + } + + if (frame->core->freq != 0.0) { + + // fundamental frequency mark with a red point. + gdk_gc_set_foreground(gc, &freq_color); + + // index of closest sample to fundamental frequency. + i = (int) rint(frame->core->freq * frame->conf->fft_size + * frame->conf->oversampling / frame->conf->sample_rate); + if ((i < frame->conf->fft_size - 1) + && (i < spectrum_size_x - 1)) { + j = (frame->core->X[i] > 1.0) ? (int) (PLOT_GAIN * log10( + frame->core->X[i])) : 0; // dB. + if (j < spectrum_size_y - 1) + gdk_draw_rectangle(pixmap, gc, TRUE, spectrum_x_margin + + i - 1, spectrum_size_y + spectrum_top_margin + - j - 1, 3, 3); + } + } + } + } + +# undef PLOT_GAIN + + gdk_gc_set_foreground(gc, &black_color); + + widget = frame->spectrum_area; + + gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE( + widget)], pixmap, 0, 0, 0, 0, spectrum_size_x + 2 + * spectrum_x_margin, spectrum_size_y + spectrum_bottom_margin + + spectrum_top_margin); + + //gdk_flush(); + + // draw note, error and frequency labels + + // ignore continuous component + if (!frame->core->running || isnan(frame->core->freq) || (frame->core->freq + < 10.0)) { + current_tone = "---"; + strcpy(error_string, "e = ---"); + strcpy(freq_string, "f = ---"); + //lingot_gauge_compute(frame->gauge, frame->conf->vr); + } else { + FLT error_cents; // do not use, unfiltered + note_index = lingot_gui_mainframe_get_closest_note_index( + frame->core->freq, frame->conf->scale, + frame->conf->root_frequency_error, &error_cents); + if (note_index == frame->conf->scale->notes) { + note_index = 0; + } + + current_tone = frame->conf->scale->note_name[note_index]; + sprintf(error_string, "e = %+2.0f cents", frame->gauge->position); + sprintf(freq_string, "f = %6.2f Hz", lingot_filter_filter_sample( + frame->freq_filter, frame->core->freq)); + } + + gtk_label_set_text(GTK_LABEL(frame->freq_label), freq_string); + gtk_label_set_text(GTK_LABEL(frame->error_label), error_string); + + char* markup = g_markup_printf_escaped( + "<span size=\"xx-large\" weight=\"bold\">%s</span>", current_tone); + gtk_label_set_markup(GTK_LABEL(frame->tone_label), markup); + g_free(markup); +} + +void lingot_gui_mainframe_change_config(LingotMainFrame* frame, + LingotConfig* conf) { + lingot_core_stop(frame->core); + lingot_core_destroy(frame->core); + + // dup. + lingot_config_copy(frame->conf, conf); + + // resize the spectrum area + g_object_unref(frame->pix_spectrum); + + int + x = ((frame->conf->fft_size > 256) ? (frame->conf->fft_size >> 1) + : 256) + 2 * spectrum_x_margin; + int y = spectrum_size_y + spectrum_top_margin + spectrum_bottom_margin; + gtk_widget_set_size_request(GTK_WIDGET(frame->spectrum_area), x, y); + frame->pix_spectrum + = gdk_pixmap_new(frame->spectrum_area->window, x, y, -1); + + gtk_scrolled_window_set_policy(frame->spectrum_scroll, + (frame->conf->fft_size > 512) ? GTK_POLICY_ALWAYS + : GTK_POLICY_NEVER, GTK_POLICY_NEVER); + gtk_widget_set_size_request(GTK_WIDGET(frame->spectrum_scroll), 260 + 2 + * spectrum_x_margin, spectrum_size_y + spectrum_bottom_margin + + spectrum_top_margin + 4 + + ((frame->conf->fft_size > 512) ? 16 : 0)); + + frame->core = lingot_core_new(frame->conf); + lingot_core_start(frame->core); + + // some parameters may have changed + lingot_config_copy(conf, frame->conf); +} diff --git a/src/lingot-gui-mainframe.h b/src/lingot-gui-mainframe.h new file mode 100644 index 0000000..16c1c3b --- /dev/null +++ b/src/lingot-gui-mainframe.h @@ -0,0 +1,81 @@ +/* + * 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 + */ + +#ifndef __LINGOT_GUI_MAIN_FRAME_H__ +#define __LINGOT_GUI_MAIN_FRAME_H__ + +#include "lingot-defs.h" +#include "lingot-core.h" +#include "lingot-gauge.h" +#include "lingot-config.h" +#include "lingot-filter.h" +#include "lingot-gui-config-dialog.h" + +#include <gtk/gtk.h> + +// Window that contains all controls, graphics, etc. of the tuner. + + +struct _LingotMainFrame { + + // gtk widgets + GtkWidget* gauge_area; + GtkWidget* spectrum_area; + GtkWidget* tone_label; + GtkWidget* view_spectrum_item; + GtkWidget* spectrum_frame; + + GtkWidget* freq_label; + GtkWidget* error_label; + + GdkPixmap* pix_spectrum; + GdkPixmap* pix_stick; + + GtkScrolledWindow* spectrum_scroll; + + LingotFilter* freq_filter; + + LingotGauge* gauge; + + LingotCore* core; + + GtkWidget* win; + + GdkColor gauge_color; + GdkColor spectrum_color; + + LingotConfigDialog* config_dialog; + LingotConfig* conf; + + // timer uids + guint visualization_timer_uid; + guint freq_computation_timer_uid; + guint gauge_computation_uid; + guint error_dispatcher_uid; +}; + +void lingot_gui_mainframe_create(int argc, char *argv[]); +void lingot_gui_mainframe_destroy(LingotMainFrame*); + +void lingot_gui_mainframe_change_config(LingotMainFrame*, LingotConfig*); + +#endif //__LINGOT_GUI_MAIN_FRAME_H__ diff --git a/src/lingot-i18n.h b/src/lingot-i18n.h new file mode 100644 index 0000000..8900d8d --- /dev/null +++ b/src/lingot-i18n.h @@ -0,0 +1,41 @@ +/* + * 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 + */ + +#ifndef LINGOTI18N_H_ +#define LINGOTI18N_H_ + +#include <libintl.h> +#include <locale.h> +#include <langinfo.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef ENABLE_NLS +#undef _ +# define _(String) (const char*) gettext (String) +#else +# define _(String) (String) +#endif + +#endif /*LINGOTI18N_H_*/ diff --git a/src/lingot-logo.xpm b/src/lingot-logo.xpm new file mode 100644 index 0000000..07c05c8 --- /dev/null +++ b/src/lingot-logo.xpm @@ -0,0 +1,364 @@ +/* + * lingot, a musical instrument tuner. + * + * Copyright (C) 2007 Matthew Blissett. + * + * 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 + */ + /* XPM */ +static const char *lingotlogo[] = { +/* columns rows colors chars-per-pixel */ +"80 80 257 2", +" c black", +". c #2E4E41EA1FB9", +"X c #2F7542BE2093", +"o c #348B44B92376", +"O c #39E8475526C6", +"+ c #3B8D48A42821", +"@ c #3E2349B12992", +"# c #42BB4C972CB8", +"$ c #47474FF73052", +"% c #48FB503430FF", +"& c #4CD853213414", +"* c #51CB55B43725", +"= c #564F5A4C3B43", +"- c #5A7D5D3C3E4E", +"; c #529657CF388C", +": c #5C5B603040B4", +"> c #5E896199422C", +", c #60A75D8440D7", +"< c #63CD64104550", +"1 c #674F67624883", +"2 c #678169444994", +"3 c #6C8E6CCB4D3A", +"4 c #68DA665B4854", +"5 c #6F0671D250C7", +"6 c #70DE6D2F4EF6", +"7 c #7EF96D2055C3", +"8 c #7D3767F552BA", +"9 c #745F74285424", +"0 c #7683792557B9", +"q c #7CB1741A57F4", +"w c #7FD8776C59C7", +"e c #7D4B7CD15BF3", +"r c #7D6D82F15F40", +"t c #79BC89195F75", +"y c #75438A075D75", +"u c #71C8929C6045", +"i c #6D1E94FB5F0A", +"p c #6DA59C5264E9", +"a c #70079ECD6897", +"s c #6FD995DC6258", +"d c #7D7F8B87653F", +"f c #7B278AA06300", +"g c #75C694C46609", +"h c #76F297BD6953", +"j c #73D69D906BB3", +"k c #7CA294916B2C", +"l c #7AEA9C466E26", +"z c #7EFA9DE57191", +"x c #67DCA2C865E8", +"c c #69B6A32F677E", +"v c #6D33A3B96AD7", +"b c #7054A9066EC7", +"n c #72F4A3E06ECE", +"m c #74D0A4A771B2", +"M c #7511AB17734D", +"N c #7AEDA601737E", +"B c #7ABCACAF7811", +"V c #7CD4AF197B14", +"C c #7EFCB2657D9F", +"Z c #84436ED0579C", +"A c #864C6FE35915", +"S c #88DA701159DC", +"D c #820F70D157E3", +"F c #85D473A35B19", +"G c #83777C2A5EF3", +"H c #89E8735A5BF1", +"J c #8C4E794E5F9F", +"K c #85587D7B6153", +"L c #8D7577E36092", +"P c #8DA17B94627A", +"I c #8F237F6B6934", +"U c #92DD7D2E6445", +"Y c #93FC7F2569D2", +"T c #812F81225F81", +"R c #838B84A0628D", +"E c #84518AE36654", +"W c #8F4983806686", +"Q c #8D5485E169D9", +"! c #8CD68D956B97", +"~ c #87268DA668AF", +"^ c #90588ED67342", +"/ c #8D9E92D06DDC", +"( c #857791776C44", +") c #84029CB772D7", +"_ c #8DE597117233", +"` c #8EA19A3D75CF", +"' c #91A88511681E", +"] c #93CC86706B43", +"[ c #949A8BF56DAD", +"{ c #995F833D67AA", +"} c #9A7C850D6B28", +"| c #9D7C8A5F6CA9", +" . c #96588B2B71A4", +".. c #9BC98BA5750F", +"X. c #9F898E617A67", +"o. c #935091BF6FC0", +"O. c #93EC961172E4", +"+. c #94C29BFD7561", +"@. c #97AE9EA37AE0", +"#. c #9A9895AD7646", +"$. c #9A109B6B7728", +"%. c #9B7E9E7579B6", +"&. c #98E9958A7AE9", +"*. c #8443A8D17B68", +"=. c #8120B2D17EA4", +"-. c #96F1A2117A1A", +";. c #9BF6A4EE7CEF", +":. c #9CD1A9A97FC5", +">. c #A17D8C156E36", +",. c #A42E8F787241", +"<. c #A1E18FC77BA7", +"1. c #A6F192E672F6", +"2. c #A39793257D69", +"3. c #AA4795E2753D", +"4. c #AE389A5D78AC", +"5. c #B30D9EE07BFF", +"6. c #B6C7A3137F4A", +"7. c #983F9F0B808C", +"8. c #8C8DAE8E8457", +"9. c #884BB0A5845A", +"0. c #84D2B602836E", +"q. c #8943B971878D", +"w. c #8D8FBB9A8BDB", +"e. c #8BD8B86388AD", +"r. c #95C6A3BD82AE", +"t. c #9F54ACF182B4", +"y. c #9A1DA87E8829", +"u. c #9168BDC38FD9", +"i. c #9701BC539207", +"p. c #A53F960382A8", +"a. c #A6DB9A3684C9", +"s. c #AC659BE684D9", +"d. c #AA7397B180C1", +"f. c #A5A4A40A861B", +"g. c #A1EDADE18417", +"h. c #A988AA688C0F", +"j. c #B018ABF193A6", +"k. c #A3CEB29F8728", +"l. c #A5AAB6FB8A31", +"z. c #A757BB5F8D2C", +"x. c #A981BF438FFC", +"c. c #AA06B6778D5A", +"v. c #AA8BC025916C", +"b. c #AFF6B2629531", +"n. c #B7BAA4C9831B", +"m. c #B9FFA681829E", +"M. c #BA8AA84D8A3E", +"N. c #B95EA8B58C7A", +"B. c #BDA2AE1797A2", +"V. c #B39EABAF95A1", +"C. c #B8B4B69B9CC1", +"Z. c #BC91AF6BA17F", +"A. c #BE02B505A4AF", +"S. c #BD15BCAEA5D5", +"D. c #B8A4BEB9A1FC", +"F. c #AC4FC5299459", +"G. c #AFB7C58C9903", +"H. c #AE87CB4B9844", +"J. c #AEC8CBF898BB", +"K. c #B413C5DE9DB1", +"L. c #B26ECE2A9CF0", +"P. c #B7B9C894A238", +"I. c #B553CFD5A1AE", +"U. c #BA0BC89DA479", +"Y. c #BB3BCA51A68D", +"T. c #BF93CD67AC76", +"R. c #BE7BC86BAA3F", +"E. c #B6DDD175A3D0", +"W. c #BA16D33EA74D", +"Q. c #BDD2D5B2AC06", +"!. c #C1CBAECD87BC", +"~. c #C315AFFE88E3", +"^. c #C5D5B3178B8F", +"/. c #C9C7B7C88EB1", +"(. c #CB2FB8E2903D", +"). c #CE7EBC989254", +"_. c #C684B6F19D30", +"`. c #D224BFD694C3", +"'. c #CA84BE10ABD3", +"]. c #C5FBB9F1A700", +"[. c #D4B0C3559720", +"{. c #D7A2C6AC99DF", +"}. c #D907C7749A12", +"|. c #DD3CCC359DE1", +" X c #C1D1CE48AF76", +".X c #C9DBC334AD40", +"XX c #C571CE8AB36D", +"oX c #CAD0CEDBB923", +"OX c #C105D785AFF8", +"+X c #C3DFD694B323", +"@X c #C548DA3FB579", +"#X c #C808DC02B94B", +"$X c #CC45D553BC6E", +"%X c #CAB8DDA2BCE1", +"&X c #C8DDD37DB87C", +"*X c #D7D2C9AFAB07", +"=X c #D951CCCEB2B0", +"-X c #DE22D322BC6C", +";X c #E140D06EA108", +":X c #E5B3D535A487", +">X c #EA6BD9F5A805", +",X c #EE1ADE3FABCD", +"<X c #E5C7D89CAE0C", +"1X c #E294D538B2DD", +"2X c #E830DB8CB90B", +"3X c #F37AE3D7AF6B", +"4X c #F619E709B22A", +"5X c #F752E925B732", +"6X c #F72CEA14BC70", +"7X c #D000D805C144", +"8X c #CEA1DFD7C17B", +"9X c #D311D51DC431", +"0X c #D29FDA28C4D8", +"qX c #D738DD50CAB4", +"wX c #DB3CDF0ECF59", +"eX c #DB3BD3BCC7E2", +"rX c #DDAADE16D202", +"tX c #CFCFE188C316", +"yX c #D2BAE2E4C6C8", +"uX c #D69AE57BCB88", +"iX c #D9EBE692CF48", +"pX c #DD68E4FCD35D", +"aX c #DE08EA70D50C", +"sX c #E3B1DB62C8FC", +"dX c #E4FEE0ABDAC4", +"fX c #E215DE2AD715", +"gX c #E5AAE293D5A7", +"hX c #E5B0E57EDC9B", +"jX c #E140EC3FD8B8", +"kX c #E45DEE6ADD4C", +"lX c #EC9CE5CAD492", +"zX c #F861EBA7C35E", +"xX c #F8ACEE49CB05", +"cX c #F913EC91C5FE", +"vX c #F958EF30CD2D", +"bX c #F9F4F083D281", +"nX c #F3AFEC22D68B", +"mX c #FA66F1D9D5EB", +"MX c #FB20F421DC8C", +"NX c #EDD8E516CCE0", +"BX c #E7CAF0BAE1C9", +"VX c #EAD5E7ADE2E7", +"CX c #ED07EA54E5DB", +"ZX c #E80DF10EE225", +"AX c #EA9DF261E5B6", +"SX c #EED8F47DEADD", +"DX c #F167F044EC18", +"FX c #F787F2AFE303", +"GX c #F246F639EE99", +"HX c #FC03F658E4BE", +"JX c #FCE7F857E952", +"KX c #FD88F974EA48", +"LX c #F4DDF7FFF1DD", +"PX c #F76AF9F8F4EF", +"IX c #F9B7FB74F7F9", +"UX c #FDC7FE48FD59", +"YX c None", +/* pixels */ +"YXYXYXYXYXYXYXYXZ S H L Y ..<.p.X.X.X.X.X.X.p.X.X.....X...&...X......... ... ... . . .[ .} [ } [ ] ] ] ] ] ] ] Q ] ' ' ' Q Y Q W W W { H H S S YXYXYXYXYXYXYXYX", +"YXYXYXYXYXH H S H ..Z.eXdXVXVXVXVXVXhXhXVXaXrXpXrXrXrXqXqXuX0X0X7X7X7X#X&X$X&XXXXXXXXX XT.T.T.Y.U.U.P.P.K.K.G.G.F.F.F.x.x.x.x.x.F.F.z.:.:.$.W H H H J YXYXYXYXYX", +"YXYXYXYXYXS H Y ].DXUXUXUXUXUXIXPXPXLXLXGXSXSXAXAXAXkXkXkXjXaXaXiXiXiXiXuXyXtXtXtX%X%X#X#X@XQ.#XQ.Q.W.W.W.E.E.E.J.J.J.J.J.J.J.J.J.J.J.J.J.J.F.%.J H H YXYXYXYXYX", +"YXYXYXYXS S p.rXUXUXUXUXUXIXIXIXPXLXGXGXSXSXAXBXAXkXkXkXaXjXaXiXiXiXiXyXyXtXtXtX%X%X#X@X@X+XQ.Q.Q.W.Q.E.E.E.L.J.H.H.J.J.J.J.J.J.J.J.J.J.J.J.J.H.k.' H H YXYXYXYX", +"YXYXYXH S p.CXUXUXUXUXUXIXIXIXLXLXGXGXSXAXAXAXBXkXkXkXkXaXaXiXiXuXuXuXyXyXtX%X%X%X#X@X@X@X+XQ.Q.Q.Q.E.E.E.J.E.J.J.H.J.J.J.J.J.J.J.J.J.J.H.J.H.J.J.x.' H H YXYXYX", +"YXYXS Z <.VXUXUXUXUXUXUXIXPXLXLXLXSXSXAXAXZXBXBXkXaXkXaXiXpXiXiXuXyXyXyXtXtX%X%X#X@X@X@XQ.Q.Q.Q.W.W.E.I.E.J.J.J.J.J.J.J.J.J.J.J.H.J.H.J.J.J.J.J.J.J.z.{ S H YXYX", +"YXH Z Y eXUXUXUXUXUXIXIXPXLXLXLXGXSXAXAXAXZXBXkXkXkXaXaXiXiXiXiXyXyXtXtXtX%X%X#X#X@XQ.@XQ.Q.W.W.W.E.I.E.J.J.J.J.H.J.J.J.J.J.J.J.H.J.H.J.J.J.J.J.J.J.J.k.J H H YX", +"YXH H ].UXUXUXUXUXUXIXIXLXLXGXDXSXAXAXAXAXkXkXkXaXaXaXaXiXiXuXyXyXyXtX%X%X%X#X@X@X@X@XQ.Q.Q.W.W.E.I.J.J.J.J.J.J.H.J.J.J.J.J.J.J.J.J.J.J.J.H.H.J.J.J.J.H.$.H H YX", +"YXA ..CXUXUXUXUXIXIXIXLXLXGXSXSXSXAXAXAXkXkXaXkXaXaXiXiXuXuXuXyXyXtXtX%X%X#X#X@XQ.Q.Q.Q.Q.W.W.E.L.L.L.H.J.J.J.J.J.J.J.J.J.J.H.J.H.J.J.J.J.J.J.J.J.J.J.J.F.W S YX", +"A S ].UXUXUXUXIXIXIXLXLXLXSXSXSXAXBXBXkXaXkXaXaXaXiXiXiXuXuXyXyXtXtX%X%X%X#X@X@XQ.@XQ.Q.W.W.E.E.L.L.H.H.H.H.J.H.H.J.H.J.H.J.H.J.J.J.J.H.J.H.H.J.H.J.J.J.F.$.S S ", +"A Y dXUXUXUXIXIXPXLXLXGXSXSXSXAXAXBXkXkXkXaXaXaXaXiXiXuXuXyXtX%X#XR.D.C.b.h.f.f.f.f.k.h.h.c.G.G.L.L.H.H.J.J.H.J.J.J.J.J.H.J.J.J.H.J.H.J.J.J.J.J.J.J.H.J.H.z.W S ", +"S p.IXUXUXUXIXIXLXLXLXSXSXAXAXAXBXBXkXkXkXaXaXpXiXiXuX7XXXD.j.X.} ' P P U U { } { ' U U L P P ' [ $.:.F.F.J.H.H.J.H.J.H.J.J.H.J.J.J.J.J.J.H.H.J.H.J.J.J.J.F.{ H ", +"S A.UXUXUXIXPXLXLXLXSXSXAXAXAXAXkXkXkXkXjXaXiXpXyXXXj.#.U L U ..s.B.'.*X*X=X2X2X2X=X1X*X{._.M.4.>.U J J ] -.k.F.H.H.J.H.J.H.J.J.J.H.J.J.H.J.J.J.J.H.J.J.J.J.3.H ", +"S eXUXIXIXPXLXLXSXGXSXAXAXAXBXkXkXkXaXaXaXiX&Xb...P Y X.B.=XsXNXnXMXmXmXmXvXvXvXxXxXvXcXcX6X6X4X,X<X}.m.>.U J { %.c.H.J.J.J.H.J.J.J.J.H.J.J.J.J.J.J.H.J.J.J.:.T ", +"L fXUXUXPXLXLXLXGXSXAXAXAXBXkXkXkXkXaXiXXXs.U U <._.-XnXMXMXHXMXbXMXbXbXvXbXxXvXxXcXcXcXcXcX6X6X6X6X6X5X,X|.!.1.U J o.l.H.H.J.J.J.H.H.J.J.J.H.J.H.J.J.J.J.J.:.T ", +"Y dXIXPXLXLXGXGXSXAXAXAXBXkXkXkXaXpX.Xa.P ] N.eXnXHXHXMXMXMXMXmXbXbXbXbXbXvXcXxXcXcXcX6X6X6X6X6X5X5X5X4X4X4X4X,X}.6.U J [ k.H.J.J.J.J.J.J.H.J.J.J.J.J.H.J.J.z.W ", +"<.VXIXLXLXGXSXSXAXAXAXAXkXkXkXaX7Xs.U Y ].sXFXHXHXMXMXMXMXbXmXmXbXbXbXvXxXxXxXzXcXcXzXzX6X5X6X5X5X5X4X4X4X3X4X4X4X3X|.6.{ J #.z.H.H.H.J.H.J.J.J.J.J.J.H.J.J.x.' ", +"X.VXLXLXLXGXSXSXAXAXBXkXkXkXiXA.Y Y B.fXFXHXHXHXMXMXMXMXMXbXbXbXbXbXvXxXxXxXzXzX6XzX6X6X6X5X6X5X4X4X4X4X4X3X4X4X4X4X4X3X:X6.U W ;.F.J.H.J.H.H.J.H.J.J.J.J.J.x.' ", +"X.hXLXGXSXSXSXAXAXBXkXkXkXoXp.L a.eXHXJXHXHXHXMXMXMXMXMXmXbXbXbXvXvXxXxXxXzXzXzXcX6X6X6X6X5X4X5X4X3X4X4X;X4.|.4X4X4X4X4X4X4X}.1.T { :.H.J.H.H.J.J.H.J.H.J.J.x.' ", +"X.hXLXSXSXAXAXAXBXBXkXkXS.Y Y '.nXKXJXHXHXMXHXMXMXMXmXmXbXbXbXbXvXvXxXxXxXzXzXzX6X6X5X6X6X5X4X4X4X4X4X3X6.Z 1.,X4X4X4X4X4X4X4X>X/.{ W 4.H.H.H.H.J.J.J.J.J.J.x.' ", +"X.hXSXSXSXAXAXBXkXkXpXV.L p.sXJXHXHXHXHXHXMXMXMXMXmXmXbXmXvXbXvXvXxXxXcXzXzXzXzX6X6X6X5X4X4X4X4X4X4X4X:X{ J S /.4X4X4X4X4X4X4X4X4X;X1.T +.F.J.J.J.H.J.H.J.J.x.' ", +"X.hXSXAXAXAXBXBXBX9Xa.L N.lXKXJXHXHXHXHXMXMXMXMXMXmXbXbXvXvXvXxXxXxXcXcXzXzX6X5X6X6X5X5X4X4X4X4X4X4X4X(.H ( e Y :X4X4X4X4X4X3X4X4X4X>X6.J | x.J.J.J.J.J.J.J.x.W ", +"X.pXSXAXBXBXkXBXqXp.L ].FXJXJXHXHXHXHXMXMXMXMXMXmXbXbXbXmXvXxXxXcXcXcX6XzXzX6X6X6X5X5X5X4X4X4X4X4X4X3X3.W N y Z m.4X4X4X4X4X4X4X4X3X4X3X^.{ W :.H.H.J.H.J.J.x.' ", +"X.fXAXAXBXBXkXqXp.Y '.FXJXJXHXHXHXHXMXMXMXMXbXbXmXbXbXbXvXvXxXxXcXcXcX6X6XzX6X5X5X5X4X4X4X4X4X4X4X4X>X{ O.=.p e J |.4X3X4X3X4X4X4X4X4X4X3X/.{ W x.J.J.J.J.J.x.{ ", +"X.rXAXZXkXkXwX7.U '.JXJXHXHXHXHXMXMXMXMXMXmXbXbXmXbXbXxXxXxXcXcXcXcX6X6X6X6X6X5X4X4X4X4X3X4X4X4X4X4X).L -.N d f Z 3.3X3X4X4X4X4X4X4X4X4X4X3X).{ ' F.J.H.H.H.F.' ", +"X.rXAXkXkXrXa.I '.JXJXJXHXHXHXMXMXMXMXMXMXmXbXbXbXbXvXxXxXxXcXcXcX6X6X6X6X6X6X4X4X4X4X4X4X4X4X4X4X4X6.P 8.f P ( T S [.3X4X3X4X4X4X4X4X4X4X4X3X/.J [ F.H.H.H.F.' ", +"..iXZXkXaXj.L ].FXJXJXHXHXHXMXMXMXMXMXbXbXbXbXbXvXvXxXxXcXzXzXzX6XcX6X6X6X5X5X4X4X4X4X4X4X4X4X4X4X>X{ ! m e Z ! u D 1.,X4X4X4X3X4X4X4X4X3X4X4X3X^.J $.H.H.H.x.{ ", +"X.qXkXkX'.L V.HXJXJXHXHXHXHXMXMXMXMXMXbXbXbXbXvXvXvXxXcXzXzXzXzX6X6X6X5X5X5X4X3X4X4X4X4X4X4X4X4X5X}.G ` a F Z P ) t H [.4X4X4X4X4X4X3X4X4X4X4X4X4X6.H :.H.H.x.W ", +"..qXBX9X} <.lXKXJXJXHXHXMXMXMXMXMXbXbXbXbXbXvXvXvXxXcXzXcXzXzXzX6X6X5X5X5X4X4X4X4X4X4X4X4X4X4X4X4Xn.P *.u D 1.P +.u G >.>X4X4X4X4X4X4X4X4X4X4X4X4X:X1.W z.J.F.W ", +"..qXrXa.U eXHXHXJXHXHXFXMXMXMXMXMXbXbXbXbXbXvXxXxXcXzXzXzXzX6X6X5X6X5X5X4X4X4X4X4X4X4X4X4X3X4X4X,X| W n r J [.| ! *.u H /.4X4X4X3X4X3X4X4X4X4X3X4X4X[.{ | F.F.W ", +"..0XA.H <.dXHXHXHXHXFXMXMXMXMXMXbXbXbXbXvXvXxXxXcXcXzXzX6X6X6X6X5X5X5X4X4X4X4X3X4X4X4X3X3X4X4X4X}.J ( v e | 3Xm.J :.a T 3.3X4X3X4X4X4X4X3X4X3X4X4X4X;X1.H ;.F.' ", +"..7XoX7.U ..'.nXFXMXMXMXMXMXbXbXbXbXbXbXvXxXcXxXcXzXzX6X6X6X6X6X6X5X4X4X3X4X4X4X4X4X4X4X4X4X4X3X!.J l g D 6.4X).G r.b T W :X4X4X3X4X4X3X4X4X4X4X>X^.| J o.:.F.W ", +"X.0XaXuXS.<.L <..XnXMXMXMXmXmXmXmXbXvXxXxXxXxXxX6XzX6X6X6X6X6X6X5X4X4X3X4X4X4X3X3X4X4X4X4X4X4X3X>.W n t Z {.4X|.J +.V d F {.4X4X4X4X4X4X4X4X>X/.>.J ' :.F.J.F.{ ", +" .$XiXuXuX%XC. .U <.*XnXmXMXvXmXvXvXxXxXcXcXcXzXzXzX6X6X6X6X4X4X4X4X4X4X4X4X4X4X4X4X4X4X4X4X3X|.U ! v e { :X4X,X' .8.d S [.4X4X4X4X4X4X>X[.1.J { :.H.J.J.J.x.W ", +"..$XiXuXuXuXtX&Xb.] L d.=XxXvXvXvXvXxXxXcXxXzX6X6X6X6X5X5X5X5X4X4X3X4X3X3X4X3X4X4X3X4X4X4X4X4X!.J ) u D 6.3X3X>X{ .9.E Z {.5X4X4X4X3X[.1.J { ;.F.J.H.H.J.J.F.W ", +"X.$XuXuXyXtXtXtX#XXXh.U U s.=XvXvXxXcXcXcXcXzX6X6X6X5X5X5X4X4X4X4X4X4X4X4X4X4X4X4X4X3X4X4X4X3X3.T n y A (.4X4X;XH _ 9.d H }.5X4X3X}.3.U W $.F.F.H.H.J.H.J.J.z.W ", +" .$XuXyXyXtX%X%X#X#X@XR.2.' U n.1XcXcXzXxX6XzX6X6X6X5X5X4X4X4X4X4X4X4X3X4X3X3X3X3X3X4X4X4X4X:XP ! v r J :X4X3X~.F -.*.r { :X3X|.6.W J %.F.J.J.J.H.H.J.J.J.J.F.T ", +"..&XyXtXtX%X%X%X%X#X+X@XOXK.%.P ' M.1XzXcX6X6X6X6X6X5X5X4X4X4X4X4X4X3X>X{.^.6.6.6.n.~.[.:X4X/.Z ) p F >.4X4X;XU K *.l F 1.|.6.U J +.l.H.H.J.J.H.H.H.H.J.J.J.F.{ ", +"| &XyX8XtX%X%X#X#X#X@X+XQ.Q.T.v.#.J } B.<X6X6X5X5X5X6X4X4X4X3X4X3X}.6.{ U J J J F H L P } n.} P n u Z ~.4X|.| Z d N E Z >.{ J o.l.H.H.H.H.H.J.J.H.H.J.J.J.J.F.W ", +" .&XtX8X%X%X#X@X@XQ.OXQ.Q.Q.Q.W.L.c.[ J | ^.<X6X5X5X4X4X4X4X4X;X6.U J [ o.R J J P J G Q [ H 8 Q j T J |.,X>.Z E l k D Z J o.k.J.J.H.H.H.H.H.H.J.H.H.J.H.J.J.F.W ", +" .XX%X%X%X#X@X@XOXOXOXQ.Q.W.W.E.I.L.H.g.[ J >./.,X3X4X3X4X4X[.{ J o.:.] K .&.r.@. .W F ^ ^ S k a w | 3X`.L d m l K Z Q g.F.J.J.J.J.H.J.H.J.J.J.J.J.J.H.H.H.F.' ", +" .XXtX%X#X#X@X+XOXOXQ.Q.E.E.E.I.L.L.L.J.G.:.Q J >.).,X4X3X`.{ U k.k.R ' y.i.i.u.w.w.8.` K 7 K l g Z 4.4X`.P d k K F @.G.J.J.J.J.H.J.J.J.H.J.H.J.H.H.J.J.H.H.l.{ ", +" . X%X#X#X@X@X+XQ.Q.Q.W.W.E.I.L.J.L.J.J.H.L.F.;.W J 1.[.}.U ' z.G.K U y.u.u.w.q.q.0.0.9.) L e a f 8 U {.(.H 7 H K t.H.H.J.J.J.J.J.J.H.J.J.H.J.J.J.J.J.J.H.J.z.W ", +"| X%X@X@X@XQ.Q.Q.Q.W.W.E.L.L.L.J.J.J.J.H.H.H.H.x.;.W U U U l.J.+.F y.u.w.q.q.q.0.=.V M m k h n G F H U U P [ :.G.H.H.H.J.J.J.J.H.J.J.J.J.J.J.H.H.J.J.J.J.J.F.W ", +"[ T.#X#XQ.@XQ.Q.Q.W.W.E.L.L.L.J.J.J.J.J.H.H.H.J.J.J.F.+.W :.H.F.K Q w.q.0.0.0.C V b v v v v v h q ! t.I %.x.J.J.J.J.J.J.J.J.J.J.J.H.J.H.H.J.J.J.J.J.J.J.H.H.F.W ", +"} T.@X@XQ.Q.Q.Q.W.E.E.L.L.L.L.J.H.J.H.J.H.H.J.J.H.J.H.F.F.H.H.z.F +.0.0.C 0.C B b c c v v v m d Z :.F.F.J.J.J.J.J.F.J.J.J.J.J.J.J.H.J.J.J.J.H.J.J.H.J.H.H.H.F.W ", +" .Y.@XQ.Q.Q.Q.W.W.E.L.L.L.L.H.J.J.J.J.H.J.J.H.J.H.J.J.J.J.J.J.k.7 ) =.V V B M c p c c v v n n K F z.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.J.J.J.H.J.J.J.H.H.H.F.' ", +"] U.Q.+XQ.Q.W.W.E.E.L.L.J.H.J.J.H.J.J.J.H.H.J.J.J.H.J.J.J.J.J.t.Z h V V M M v x c v v v b m a q Q J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.J.J.J.H.J.H.J.H.J.J.H.H.z.' ", +" .U.Q.Q.Q.W.W.E.J.I.L.J.J.J.J.J.H.J.J.J.J.H.H.J.J.J.J.H.H.J.J.k.Z g b b n c c x v v v v n m k Z @.J.J.J.J.J.H.J.J.H.J.J.H.J.J.H.H.J.J.J.J.H.J.J.J.J.H.J.J.J.F.W ", +"} U.Q.Q.W.W.I.E.E.J.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.H.J.J.J.J.J.z.F r c v c c c v v v v v m n G F z.J.J.J.H.J.J.H.H.J.H.J.J.H.H.J.J.J.H.J.J.J.H.J.H.J.J.J.H.J.F.W ", +"! P.Q.W.W.I.E.E.J.J.J.J.J.J.J.J.H.J.J.J.H.J.H.H.J.J.J.H.H.J.H.H.o.w i x x c c v v m v m m h F O.H.J.J.J.H.J.H.J.J.J.H.H.J.J.J.J.H.J.H.J.J.J.J.J.J.J.H.J.H.H.F.' ", +"] P.W.E.E.E.L.J.J.J.J.J.J.J.J.J.J.J.H.H.J.J.J.J.H.H.J.J.H.H.H.J.z.J w p c c v v v v m m j G K z.J.J.J.J.J.J.J.J.J.H.J.J.H.H.J.H.J.J.J.J.H.J.H.J.J.H.J.J.H.H.z.' ", +"' K.W.E.I.J.J.J.J.J.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.J.J.J.J.H.H.J.J.;.F r p v v b m n m j e F :.H.J.J.J.H.J.J.H.J.H.J.J.H.J.J.H.J.H.J.J.J.J.J.J.J.H.J.H.J.J.J.z.W ", +"' K.E.I.E.J.J.H.J.J.J.J.J.J.J.J.H.J.J.J.H.H.J.H.H.J.H.J.J.J.J.J.J.H.;.J F r g h h g f K F -.H.H.J.J.J.J.H.J.J.J.J.H.J.J.H.J.J.H.J.J.J.H.H.J.J.J.J.J.J.J.J.J.F.' ", +"] G.I.I.J.J.J.H.H.J.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.J.J.J.H.H.J.H.J.J.H.k.! F F F F Z 7 I t.J.J.J.J.H.J.H.J.J.J.J.J.H.J.H.H.J.J.H.H.J.H.J.J.J.J.J.H.J.J.J.J.J.F.W ", +"] G.I.J.G.@.O.l.F.J.J.J.J.J.J.J.H.J.J.J.H.J.H.J.J.H.J.H.J.J.J.J.J.J.J.J.F.z.+.o.o.O.y.F.J.J.J.H.J.J.J.J.J.H.J.H.H.J.J.H.J.J.H.J.J.J.J.J.H.J.J.J.J.J.J.J.J.J.F.W ", +"' F.L.L.-.; # 5 F.H.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.H.J.J.J.J.H.J.F.F.H.J.J.H.H.F.' ", +"' F.J.J._ # . 1 x.J.J.J.J.J.J.J.J.J.J.J.H.J.H.J.H.J.H.J.J.J.J.J.J.J.J.F.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.H.J.H.J.J.J.J.J.J.H.J.J.F.T 3 -.H.L.H.H.z.' ", +"' x.J.J.+.& o e F.J.J.J.J.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.H.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.H.J.J.J.J.H.J.H.J.H.H.J.F.+.$ o - +.:.z.H.z.' ", +"Q F.J.J.+.$ + T G.F.J.J.J.J.J.J.J.J.J.J.J.J.J.H.J.H.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.F.J.J.J.J.J.J.H.J.J.H.J.J.J.H.H.z.O.r - o X o $ * 3 :.F.' ", +"' z.H.J._ $ + ~ F.J.J.J.J.J.F.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.F.F.F.J.J.J.J.J.J.J.J.H.H.J.J.J.H.J.J.J.J.F.0 # o X X X o + + & #.F.' ", +"' F.J.J._ # + o.J.J.J.J.J.J.J.J.J.J.J.J.z./ E :.F.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.F.t.T 0 T t.J.J.J.J.J.J.J.H.F.F.F.H.J.H.J.J.J.F.T > > $ . O 2 T ! +.F.z.' ", +"U F.J.J._ # + O.J.J.J.J.J.J.J.J.J.J.J.J.+.& O 9 z.J.J.J.H.z.-.l.J.J.F.J.J.J.J.J.J.J.J.-.> O . + 0 F.J.J.J.J.J.x./ 5 2 5 ` z.J.J.J.H.H.F.l.:.1 . $ +.J.J.J.J.x.Q ", +"T F.J.J./ # + +.J.J.J.J.J.J.J.J.J.J.J.J.:.6 < / H.J.J.J.z.0 ; e k./ r -.F.J.J.J.J.J.z.5 o @ O o 5 x.J.J.J.J.F.~ & + + o # 0 F.J.J.J.H.H.H.F.> . * :.J.J.H.J.x.Q ", +"U z.L.H./ # + +.J.J.J.J.J.J.J.H.J.J.J.H.-.< < ;.L.J.J.J.:.* . - < # O ; / J.H.H.J.J.:.- o 7 = X 0 H.J.J.J.J.F.4 X - 0 & X % / F.H.H.J.J.J.:.- X = l.J.J.J.J.z.J ", +"J l.J.J.! @ # +.J.J.J.J.J.J.J.H.J.H.J.F.9 o # T J.J.J.J.:.* . o X # # X < F.H.H.J.J.:.- O , % X 6 F.J.J.J.J.:.- o T F.! & o = +.J.H.J.J.J.;.& X < z.J.J.J.J.l.H ", +"H :.L.H.! + + O.H.H.J.J.J.J.J.H.J.J.J.F.1 X * ;.J.J.J.J.+.$ . . # T G o % $.H.H.J.J.H.9 o X X X - :.J.J.H.H.F.- O r H.J.T O o T F.J.J.J.J.! + o 5 F.J.H.J.J.k.H ", +"H $.J.H.T o + 6 T ! +.+.:.k.F.H.J.J.J.:.= X - z.J.J.J.J.! @ . O 9 F.~ o + / H.H.J.J.J.:.6 - - # @ / J.J.H.H.F.9 O > :.:.T O X T F.J.J.J.J.~ O O T F.J.H.J.J.$.H ", +"S ! H.l.2 X X o @ # @ # * - e z.J.J.J.;.& o 3 F.H.H.H.H.T # . = :.H./ # # T F.J.J.J.L.J.F.F.:.r O 3 :.H.J.J.H.$.; + > < # o - +.J.H.J.J.F.T O O / H.H.H.J.F.] S ", +"S J l.F.T < < 3 5 5 3 1 - & 4 -.J.J.J.:.1 - _ H.H.H.H.H.-.2 < ! H.H.:.2 * T F.J.H.F.:.H.H.H.J.+.% # / H.J.J.H.H.+.< & & = 3 +.F.J.J.J.J.H.+.3 3 g.J.H.H.J.z.J S ", +"S H 3.H.F.l.l.F.F.F.H.x.k.-.:.H.J.J.J.H.k.:.H.J.H.H.H.H.F.F.l.H.H.H.H.:.-.F.H.J.H.T < T F.H.H.F.3 o 3 F.J.H.H.H.J.z.-.;.:.F.H.H.J.J.H.J.J.H.:.H.H.H.J.J.J.+.H S ", +"YXS T F.J.H.H.H.J.J.J.J.J.J.J.J.J.J.J.J.H.H.H.H.J.H.H.H.H.J.J.H.H.H.H.H.H.H.J.J.F.r o & T :.:.:.> o 5 F.J.H.H.H.J.J.J.J.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.J.x.' H YX", +"YXH S $.H.H.H.J.J.J.J.H.J.J.J.J.J.H.J.H.J.J.J.J.J.J.J.J.H.H.J.J.J.J.H.J.H.J.J.J.J.:.3 # + = = * @ - +.H.J.H.J.H.J.H.J.J.J.J.J.J.H.J.H.J.J.J.J.J.J.J.J.H.$.H H YX", +"YXH H J k.J.H.H.J.H.J.J.J.J.J.J.J.J.J.J.H.J.H.H.J.H.J.H.J.J.H.J.H.J.J.J.H.J.H.J.J.L.:.~ 9 > 2 3 T :.H.H.J.J.J.J.J.J.H.J.H.J.H.J.J.J.J.J.J.H.H.J.H.J.L.:.P S H YX", +"YXYXH S Q z.J.J.J.H.J.H.J.J.J.J.H.J.H.J.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.J.J.H.J.J.J.J.J.J.F.:.F.F.H.H.H.H.H.J.H.H.J.J.J.J.J.J.H.H.J.J.H.J.H.H.H.H.H.H.F.W S H YXYX", +"YXYXYXH H [ x.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.H.J.H.J.H.J.J.J.J.H.H.J.J.J.J.J.J.H.J.J.J.J.J.L.H.H.H.H.H.H.J.J.H.H.H.J.H.J.H.J.H.H.H.J.H.J.H.H.H.H.H.F.{ H H YXYXYX", +"YXYXYXYXH H Q k.H.J.J.J.J.J.J.J.H.J.J.J.H.H.H.H.J.H.H.H.J.J.J.J.J.J.J.J.H.H.H.H.J.J.J.J.H.H.H.H.H.H.H.H.H.H.H.H.J.J.H.H.H.H.H.H.J.J.J.J.J.J.H.H.:./ H H YXYXYXYX", +"YXYXYXYXYXH H P %.x.J.J.J.J.J.J.J.J.J.J.H.F.H.H.J.H.H.H.J.J.J.J.J.J.J.J.H.H.H.F.J.J.J.J.H.F.H.H.H.H.H.H.J.J.H.H.J.J.H.F.J.H.H.H.J.J.J.L.J.J.F.$.W H H YXYXYXYXYX", +"YXYXYXYXYXH H H J U #.:.z.z.x.x.F.F.F.F.F.F.z.F.F.F.F.l.F.F.F.F.F.z.F.F.F.F.F.F.F.F.F.F.F.F.z.F.F.F.z.F.F.F.F.z.F.F.F.F.F.F.F.z.F.F.z.:.:.$.W H S S S YXYXYXYXYX", +"YXYXYXYXYXYXYXYXJ S S J J ' ' ' W ' { T W { W W W W W W W W W W W W W ' ' T W { T { W W { W W W W W W W W W W W W W W W W W W W W { W J W H S S YXYXYXYXYXYXYXYX" +}; diff --git a/src/lingot-msg.c b/src/lingot-msg.c new file mode 100644 index 0000000..8cd8864 --- /dev/null +++ b/src/lingot-msg.c @@ -0,0 +1,91 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <pthread.h> + +#include "lingot-msg.h" + +#define MAX_MESSAGES 5 + +char message[MAX_MESSAGES][1000]; +message_type_t message_type[MAX_MESSAGES]; + +int front = 0, rear = 0; + +pthread_mutex_t message_queue_mutex = PTHREAD_MUTEX_INITIALIZER; + +void lingot_msg_add_error(const char* msg) { + lingot_msg_add(msg, ERROR); +} + +void lingot_msg_add_warning(const char* msg) { + lingot_msg_add(msg, WARNING); +} + +void lingot_msg_add_info(const char* msg) { + lingot_msg_add(msg, INFO); +} + +void lingot_msg_add(const char* msg, message_type_t type) { + + pthread_mutex_lock(&message_queue_mutex); + if (front == ((rear + 1) % MAX_MESSAGES)) { + perror("The messages array is full!\n"); + } else { + // check if the message is already in the queue + int duplicated = 0; + int i = front; + while (i != rear) { + i = (i + 1) % MAX_MESSAGES; + if (!strcmp(message[i], msg)) { + duplicated = 1; + printf("duplicated message: %s\n", msg); + break; + } + } + + if (!duplicated) { + rear = ((rear + 1) % MAX_MESSAGES); + strcpy(message[rear], msg); + message_type[rear] = type; + } + } + pthread_mutex_unlock(&message_queue_mutex); +} + +int lingot_msg_get(char** msg, message_type_t* type) { + int result = 0; + *msg = NULL; + + pthread_mutex_lock(&message_queue_mutex); + if (front != rear) { + front = (front + 1) % MAX_MESSAGES; + *msg = strdup(message[front]); + *type = message_type[front]; + result = 1; + } + pthread_mutex_unlock(&message_queue_mutex); + + return result; +} diff --git a/src/lingot-msg.h b/src/lingot-msg.h new file mode 100644 index 0000000..6e28964 --- /dev/null +++ b/src/lingot-msg.h @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#ifndef __LINGOT_MESSAGES_H__ +#define __LINGOT_MESSAGES_H__ + +// asynchronous message handling + +// message types +typedef enum message_type_t { + ERROR = 0, WARNING = 1, INFO = 2 +} message_type_t; + +// add messages to the queue +void lingot_msg_add(const char* message, message_type_t type); +void lingot_msg_add_error(const char* message); +void lingot_msg_add_warning(const char* message); +void lingot_msg_add_info(const char* message); + +// gets a message from the queue, it returns 0 if no messages are available +int lingot_msg_get(char** msg, message_type_t* type); + +#endif diff --git a/src/lingot-signal.c b/src/lingot-signal.c new file mode 100644 index 0000000..b94ac25 --- /dev/null +++ b/src/lingot-signal.c @@ -0,0 +1,165 @@ +/* + * 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 <math.h> + +#include "lingot-signal.h" + +/* + peak identification functions. + */ + +FLT lingot_signal_get_noise_threshold(LingotConfig* conf, FLT w) { + //return 0.5*(1.0 - 0.9*w/M_PI); + return pow(10.0, (conf->noise_threshold_db * (1.0 - 0.9 * w / M_PI)) / 10.0); + //return conf->noise_threshold_un; +} + +//--------------------------------------------------------------------------- + +/* returns the index of the maximun of the buffer x of size N */ +void lingot_signal_get_max(FLT *x, int N, int* Mi) { + register int i; + FLT M; + + M = -1.0; + *Mi = -1; + + for (i = 0; i < N; i++) { + + if (x[i] > M) { + M = x[i]; + *Mi = i; + } + } +} + +//--------------------------------------------------------------------------- + +int lingot_signal_is_peak(LingotConfig* conf, FLT* x, int index) { + register unsigned int j; + //static FLT delta_w_FFT = 2.0*M_PI/conf->FFT_SIZE; // resolution in rads + + // a peak must be greater than noise threshold. + if (x[index] < conf->noise_threshold_nu) + return 0; + + for (j = 0; j < conf->peak_half_width; j++) { + if (x[index + j] < x[index + j + 1]) + return 0; + if (x[index - j] < x[index - j - 1]) + return 0; + } + return 1; +} + +//--------------------------------------------------------------------------- + +// search the fundamental peak given the spd and its 2nd derivative +int lingot_signal_get_fundamental_peak(LingotConfig* conf, FLT *x, FLT* d2x, + int N) { + register unsigned int i, j, m; + int p_index[conf->peak_number]; + + // at this moment there is no peaks. + for (i = 0; i < conf->peak_number; i++) + p_index[i] = -1; + + unsigned int lowest_index = (unsigned int) ceil(conf->min_frequency * (1.0 + * conf->oversampling / conf->sample_rate) * conf->fft_size); + + if (lowest_index < conf->peak_half_width) + lowest_index = conf->peak_half_width; + + // I'll get the PEAK_NUMBER maximum peaks. + for (i = lowest_index; i < N - conf->peak_half_width; i++) + if (lingot_signal_is_peak(conf, x, i)) { + + // search a place in the maximums buffer, if it doesn't exists, the + // lower maximum is candidate to be replaced. + m = 0; // first candidate. + for (j = 0; j < conf->peak_number; j++) { + if (p_index[j] == -1) { + m = j; // there is a place. + break; + } + + if (d2x[p_index[j]] < d2x[p_index[m]]) + m = j; // search the lowest. + } + + if (p_index[m] == -1) + p_index[m] = i; // there is a place + else if (d2x[i] > d2x[p_index[m]]) + p_index[m] = i; // if greater + } + + FLT maximum = 0.0; + int maximum_index = -1; + + // search the maximum peak + for (i = 0; i < conf->peak_number; i++) + if ((p_index[i] != -1) && (x[p_index[i]] > maximum)) { + maximum = x[p_index[i]]; + maximum_index = p_index[i]; + } + + if (maximum_index == -1) + return N; + + // all peaks much lower than maximum are deleted. + for (i = 0; i < conf->peak_number; i++) + if ((p_index[i] == -1) || (conf->peak_rejection_relation_nu + * x[p_index[i]] < maximum)) + p_index[i] = N; // there are available places in the buffer. + + // search the lowest maximum index. + for (m = 0, j = 0; j < conf->peak_number; j++) { + if (p_index[j] < p_index[m]) + m = j; + } + + return p_index[m]; +} + +//--------------------------------------------------------------------------- + +// generates a Hamming window of N samples +void lingot_signal_window(int N, FLT* out, window_type_t window_type) { + register int i; + switch (window_type) { + case RECTANGULAR: + for (i = 0; i < N; i++) + out[i] = 1.0; + break; + case HANNING: + for (i = 0; i < N; i++) + out[i] = 0.5 * (1 - cos((2.0 * M_PI * i) / (N - 1))); + break; + case HAMMING: + for (i = 0; i < N; i++) + out[i] = 0.53836 - 0.46164 * cos((2.0 * M_PI * i) / (N - 1)); + break; + default: + break; + } +} diff --git a/src/lingot-signal.h b/src/lingot-signal.h new file mode 100644 index 0000000..53c9383 --- /dev/null +++ b/src/lingot-signal.h @@ -0,0 +1,48 @@ +/* + * 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 + */ + +#ifndef __LINGOT_SIGNAL_H__ +#define __LINGOT_SIGNAL_H__ + +/* + peak identification functions. + */ + +#include "lingot-defs.h" +#include "lingot-config.h" + +// returns noise threshold at a given frequency w. +FLT lingot_signal_get_noise_threshold(LingotConfig*, FLT w); + +// returns if buffer has a peak at given index +int lingot_signal_is_peak(LingotConfig*, FLT* buffer, int index); + +// returns the maximum index. +void lingot_signal_get_max(FLT *buffer, int N, int* Mi); + +// returns the index of the peak that carries the fundamental freq. +int lingot_signal_get_fundamental_peak(LingotConfig*, FLT *x, FLT* y, int N); + +// generates a Hamming window of N samples +void lingot_signal_window(int N, FLT* out, window_type_t window_type); + +#endif /*__LINGOT_SIGNAL_H__*/ diff --git a/src/lingot.c b/src/lingot.c new file mode 100644 index 0000000..d383833 --- /dev/null +++ b/src/lingot.c @@ -0,0 +1,149 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> + +#include "lingot-defs.h" +#include "lingot-config.h" +#include "lingot-gui-mainframe.h" +#include "lingot-i18n.h" + +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <glib.h> + +char CONFIG_FILE_NAME[100]; + +//void print_card_list(void) { +// int status; +// int card = -1; // use -1 to prime the pump of iterating through card list +// char* longname = NULL; +// char* shortname = NULL; +// +// if ((status = snd_card_next(&card)) < 0) { +// error("cannot determine card number: %s", snd_strerror(status)); +// return; +// } +// if (card < 0) { +// error("no sound cards found"); +// return; +// } +// while (card >= 0) { +// printf("Card %d:", card); +// if ((status = snd_card_get_name(card, &shortname)) < 0) { +// error("cannot determine card shortname: %s", snd_strerror(status)); +// break; +// } +// if ((status = snd_card_get_longname(card, &longname)) < 0) { +// error("cannot determine card longname: %s", snd_strerror(status)); +// break; +// } +// printf("\tLONG NAME: %s\n", longname); +// printf("\tSHORT NAME: %s\n", shortname); +// if ((status = snd_card_next(&card)) < 0) { +// error("cannot determine card number: %s", snd_strerror(status)); +// break; +// } +// } +//} + +int main(int argc, char *argv[]) { + +#ifdef ENABLE_NLS + bindtextdomain(GETTEXT_PACKAGE, LINGOT_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); +#endif + + // default config file. + sprintf(CONFIG_FILE_NAME, "%s/" CONFIG_DIR_NAME DEFAULT_CONFIG_FILE_NAME, + getenv("HOME")); + + // TODO: indicate complete config file path + if ((argc > 3) || (argc == 2)) { + printf("\nusage: lingot [-c config]\n\n"); + return -1; + } else if (argc > 1) { + int c; + // int digit_optind = 0; + + while (1) { + // int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { { "config", 1, 0, 'c' }, { + 0, 0, 0, 0 } }; + + c = getopt_long(argc, argv, "c:", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'c': + sprintf(CONFIG_FILE_NAME, "%s/%s%s.conf", getenv("HOME"), + CONFIG_DIR_NAME, optarg); + printf("using config file %s\n", CONFIG_FILE_NAME); + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); + break; + } + } + } + + // if config file doesn't exists, i will create it. + FILE* fp; + if ((fp = fopen(CONFIG_FILE_NAME, "r")) == NULL) { + + char config_dir[100]; + sprintf(config_dir, "%s/.lingot/", getenv("HOME")); + printf("creating directory %s ...\n", config_dir); + mkdir(config_dir, 0777); // creo el directorio. + printf("creating file %s ...\n", CONFIG_FILE_NAME); + + // new configuration with default values. + LingotConfig* new_conf = lingot_config_new(); + lingot_config_restore_default_values(new_conf); + lingot_config_save(new_conf, CONFIG_FILE_NAME); + lingot_config_destroy(new_conf); + + printf("ok\n"); + + } else + fclose(fp); + +// print_card_list(); + + lingot_gui_mainframe_create(argc, argv); + + return 0; +} |
