diff --git a/CMakeLists.txt b/CMakeLists.txt index 64d0033..3c68231 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(external/magic_enum) add_subdirectory(external/argparse) add_subdirectory(external/log4cxx) + file(GLOB_RECURSE sources src/*.cpp src/*.h) file(GLOB_RECURSE data resources/*) diff --git a/scripts/raw-to-wav.sh b/scripts/raw-to-wav.sh new file mode 100755 index 0000000..e66d024 --- /dev/null +++ b/scripts/raw-to-wav.sh @@ -0,0 +1,8 @@ +#! /bin/sh + +set -eux + +input_file="$1" +output_file="$2" + +sox -r 44100 -e signed-integer -b 16 -c 1 ${input_file} ${output_file} \ No newline at end of file diff --git a/scripts/samples.py b/scripts/samples.py index aa6b1b5..3307245 100755 --- a/scripts/samples.py +++ b/scripts/samples.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 -from tones import SQUARE_WAVE +from tones import SQUARE_WAVE, SINE_WAVE from tones.mixer import Mixer import csv import math @@ -14,7 +14,8 @@ def frequency_to_midi(frequency): f = 440 * 2 ^ (n - 69)/12 ''' note = 12 * (math.log(frequency/220)/math.log(2)) + 57 - return round(note) + #return round(note) + return note tones = {} with open('tones.csv', newline='') as csvfile: @@ -26,6 +27,27 @@ for tone in tones: print(f"{tone} is MIDI #{frequency_to_midi(tones[tone])}") mixer = Mixer(44100, 1) - mixer.create_track(0, SQUARE_WAVE) + mixer.create_track(0, SINE_WAVE) mixer.add_tone(0, frequency=tones[tone], duration=1.0) - mixer.write_wav(f'samples/{tone}.wav') \ No newline at end of file + mixer.write_wav(f'samples/{tone}.wav') + + +mixer = Mixer(44100, 1) +mixer.create_track(0, SQUARE_WAVE) +mixer.add_tone(0, frequency=440, duration=1.0) +mixer.write_wav(f'samples/MIDI-69-square.wav') + +mixer = Mixer(44100, 1) +mixer.create_track(0, SINE_WAVE) +mixer.add_tone(0, frequency=440, duration=1.0) +mixer.write_wav(f'samples/MIDI-69-sine.wav') + +mixer = Mixer(44100, 1) +mixer.create_track(0, SQUARE_WAVE) +mixer.add_tone(0, frequency=261.6256, duration=1.0) +mixer.write_wav(f'samples/MIDI-60-square.wav') + +mixer = Mixer(44100, 1) +mixer.create_track(0, SINE_WAVE) +mixer.add_tone(0, frequency=261.6256, duration=1.0) +mixer.write_wav(f'samples/MIDI-60-sine.wav') \ No newline at end of file diff --git a/scripts/selcal-detect.py b/scripts/selcal-detect.py index 8041239..cc080c9 100755 --- a/scripts/selcal-detect.py +++ b/scripts/selcal-detect.py @@ -14,6 +14,14 @@ with open('tones.csv', newline='') as csvfile: for row in reader: tones[row['designator']] = float(row['frequency']) +''' +def freq_of_key(midi_key): + return 440.0 * (2 ** ((midi_key - 69)/12)) + +tones = {} +for c in range(65, 90): + tones[c] = freq_of_key(c) +''' # Shamelessly lifted from # https://scipy.github.io/old-wiki/pages/Cookbook/ButterworthBandpass @@ -98,6 +106,14 @@ if __name__ == '__main__': line = plot.plot(correlation, pen=pg.mkPen(color=color), fillLevel=0.1, name=tone) legend.addItem(line, tone) + y_max = max(line.getData()[1]) # Maximum y-value + x_max = line.getData()[0][np.argmax(line.getData()[1])] # Corresponding x-coordinate + + label = pg.TextItem(html=f'
{line.opts["name"]}
', anchor=(0.5, 0.5)) + plot.addItem(label) + label.setPos(x_max, y_max) + label.setZValue(100) # Ensure label is above other items + plot.setLabel('left', 'Signal Correlation') plot.setLabel('bottom', 'Time (samples)') plot.showGrid(x=True, y=True) diff --git a/soundfonts/basic.sf2 b/soundfonts/basic.sf2 new file mode 100644 index 0000000..06c11ac Binary files /dev/null and b/soundfonts/basic.sf2 differ diff --git a/soundfonts/sine.sf2 b/soundfonts/sine.sf2 index bf82f2d..bd020df 100644 Binary files a/soundfonts/sine.sf2 and b/soundfonts/sine.sf2 differ diff --git a/src/main.cpp b/src/main.cpp index 3c0eea7..44b4127 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,6 +92,7 @@ int main(int argc, char** argv) { Synth synth; + auto tunings = getSelcalTunings(); if (!synth.setTuning(tunings)) { LOG4CXX_ERROR(logger, "Failed to set SELCAL tuning on synth"); @@ -127,7 +128,10 @@ int main(int argc, char** argv) { SELCAL::Code code{parser.get("code")}; + for (auto& group : code.getGroups()) { + int midi = static_cast(group[0]); + std::cout << "Playing MIDI " << midi << std::endl; fluid_synth_noteon(raw_synth, 0, static_cast(group[0]), 127); fluid_synth_noteon(raw_synth, 1, static_cast(group[1]), 127); std::this_thread::sleep_for(std::chrono::duration(toneDuration)); @@ -137,7 +141,17 @@ int main(int argc, char** argv) { std::this_thread::sleep_for(std::chrono::duration(silenceDuration)); } + /* + for (int i = 65; i < 90; ++i) { + fluid_synth_noteon(raw_synth, 0, i, 80); // Soundfont root key is TWELVE OUT?? + std::this_thread::sleep_for(std::chrono::duration(0.1)); + + // Silence between tone groups + fluid_synth_all_notes_off(raw_synth, ALL_CHANNELS); + std::this_thread::sleep_for(std::chrono::duration(0.01)); + }*/ + // Wait long enough for the full tone to play before tearing down. using namespace std::chrono_literals; - std::this_thread::sleep_for(500ms); + //std::this_thread::sleep_for(500ms); } diff --git a/src/synth.cpp b/src/synth.cpp index b651117..54bf8c8 100644 --- a/src/synth.cpp +++ b/src/synth.cpp @@ -14,6 +14,15 @@ Synth::Synth() { throw new std::runtime_error("Unable to create fluid synth"); } + fluid_settings_setstr(settings, "audio.driver", "file"); + fluid_settings_setstr(settings, "audio.file.name", "output.raw"); + fluid_settings_setstr(settings, "audio.file.format", "s16"); + fluid_settings_setstr(settings, "audio.file.type", "raw"); + fluid_settings_setnum(settings, "synth.sample-rate", 44100); + fluid_settings_setnum(settings, "synth.gain", 1.0); + fluid_settings_setint(settings, "synth.chorus.active", 0); + fluid_settings_setint(settings, "synth.reverb.active", 0); + adriver = new_fluid_audio_driver(settings, synth); if (adriver == nullptr) { throw new std::runtime_error("Unable to create audio driver");