Skip to content

Commit

Permalink
Expanded Microtonal Support (BespokeSynth#278)
Browse files Browse the repository at this point in the history
  • Loading branch information
baconpaul authored Sep 28, 2021
1 parent c9efdd1 commit c7f6e18
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
[submodule "libs/pybind11"]
path = libs/pybind11
url = https://github.com/pybind/pybind11
[submodule "libs/tuning-library"]
path = libs/tuning-library
url = https://github.com/surge-synthesizer/tuning-library
[submodule "libs/oddsound-mts/MTS-ESP"]
path = libs/oddsound-mts/MTS-ESP
url = https://github.com/ODDSound/MTS-ESP
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ add_subdirectory(libs/leathers)
add_subdirectory(libs/json)
add_subdirectory(libs/nanovg)

add_subdirectory(libs/tuning-library EXCLUDE_FROM_ALL)
add_subdirectory(libs/oddsound-mts EXCLUDE_FROM_ALL)

# Generate build time info for the cpp
configure_file(${CMAKE_SOURCE_DIR}/Source/VersionInfo.cpp.in
Expand Down Expand Up @@ -592,13 +594,19 @@ target_link_libraries(BespokeSynth PRIVATE
bespoke::nanovg
bespoke::xwax
bespoke::leathers

pybind11::pybind11

tuning-library
oddsound-mts

juce::juce_audio_basics
juce::juce_audio_devices
juce::juce_audio_formats
juce::juce_audio_processors
juce::juce_opengl
juce::juce_osc
juce::juce_gui_basics)
juce::juce_gui_basics
)

target_link_libraries(BespokeSynth PRIVATE ${Python_LIBRARIES})
168 changes: 166 additions & 2 deletions Source/Scale.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include "SynthGlobals.h"
#include "ModularSynth.h"
#include "ofxJSONElement.h"
#include "Tunings.h"
#include "libMTSClient.h"

Scale* TheScale = nullptr;

Expand All @@ -50,6 +52,15 @@ Scale::Scale()
SetName("scale");
}

Scale::~Scale()
{
if (oddsound_mts_client)
{
MTS_DeregisterClient(oddsound_mts_client);
oddsound_mts_client = nullptr;
}
}

void Scale::CreateUIControls()
{
IDrawableModule::CreateUIControls();
Expand All @@ -60,7 +71,9 @@ void Scale::CreateUIControls()
mTetEntry = new TextEntry(this,"tet",4,24,2,&mTet,0,99);
mReferenceFreqEntry = new TextEntry(this,"tuning",4,43,3,&mReferenceFreq,1,999);
mReferencePitchEntry = new TextEntry(this,"note",72,43,3,&mReferencePitch,0,127);

mLoadSCL = new ClickButton(this, "Load SCL", 4, 62);
mLoadKBM = new ClickButton(this, "Load KBM", 74, 62);

mTetEntry->DrawLabel(true);
mReferenceFreqEntry->DrawLabel(true);
mReferencePitchEntry->DrawLabel(true);
Expand All @@ -83,6 +96,8 @@ void Scale::CreateUIControls()
mIntonationSelector->AddLabel("just", kIntonation_Just);
mIntonationSelector->AddLabel("pyth", kIntonation_Pythagorean);
mIntonationSelector->AddLabel("mean", kIntonation_Meantone);
mIntonationSelector->AddLabel("sclkbm", kIntonation_SCLKBM);
mIntonationSelector->AddLabel("oddsound", kIntonation_ODDSOUNDMTS);
}

void Scale::Init()
Expand Down Expand Up @@ -169,6 +184,33 @@ float Scale::PitchToFreq(float pitch)

break;
}
case kIntonation_SCLKBM: {
auto ip = (int) pitch + 128;
if (ip < 0 || ip > 256) return 440;

// Interpolate in log space
auto lt = mTuningTable[ip];
auto nt = mTuningTable[ip+((ip != 255) ? 1 : 0 )];
auto fp = (pitch+128) - ip;
auto interplt = (1-fp) * lt + fp * nt;

// Then pow2 it and multiply by freq0
return Pow2(interplt) * Tunings::MIDI_0_FREQ;
}
break;
case kIntonation_ODDSOUNDMTS: {
if (oddsound_mts_client && MTS_HasMaster(oddsound_mts_client)) {
if (pitch < 0 || pitch > 127) {
// Improve this obviously
return Pow2((pitch - mReferencePitch) / mTet) * mReferenceFreq;
} else {
return MTS_NoteToFrequency(oddsound_mts_client, (int) pitch, 0);
}
} else {
return Pow2((pitch - mReferencePitch) / mTet) * mReferenceFreq;
}
}
break;
default:
assert(false);
}
Expand Down Expand Up @@ -332,6 +374,8 @@ void Scale::DrawModule()
mReferenceFreqEntry->Draw();
mReferencePitchEntry->Draw();
mIntonationSelector->Draw();
mLoadSCL->Draw();
mLoadKBM->Draw();
}

vector<int> Scale::GetPitchesForScale(string type)
Expand Down Expand Up @@ -438,7 +482,7 @@ void Scale::UpdateTuningTable()
tunings[3] = 32.0f/27.0f;
tunings[4] = 81.0f/64.0f;
tunings[5] = 4.0f/3.0f;
tunings[6] = 1024.0f/729.0f;
tunings[6] = 729.f/512.f;
tunings[7] = 3.0f/2.0f;
tunings[8] = 128.0f/81.0f;
tunings[9] = 27.0f/16.0f;
Expand Down Expand Up @@ -483,8 +527,63 @@ void Scale::UpdateTuningTable()
int octave = floor((i-128) / 12.0f);
float ratio = powf(2,octave);
mTuningTable[i] = tunings[(i-128+144)%12] * ratio; //+144 to keep modulo arithmetic positive
std::cout << i << " " << i - 128 << " " << mTuningTable[i] << std::endl;
auto t = tunings[(i-128+144)%12];
}

/*
* At this point we need to make sure the tuning table matches the reference pitch
*/
auto idx = (int)mReferencePitch + 128;
if (idx >= 0 && idx < 256) {
auto ttRP = mTuningTable[idx];
auto lf = mReferenceFreq / Tunings::MIDI_0_FREQ;
auto ratio = lf / ttRP;
for (int i=0; i<256; ++i)
mTuningTable[i] *= ratio;
}
}
if( mIntonation == kIntonation_ODDSOUNDMTS)
{
if (oddsound_mts_client == nullptr)
{
ofLog() << "Connecting to oddsound mts";
oddsound_mts_client = MTS_RegisterClient();
}

if (oddsound_mts_client == nullptr)
{
mIntonation = kIntonation_Equal;
return;
}
}
if (mIntonation== kIntonation_SCLKBM)
{
try {
Tunings::Scale scale;
Tunings::KeyboardMapping mapping;
if (mSclContents.empty())
scale = Tunings::evenTemperament12NoteScale();
else
scale = Tunings::parseSCLData(mSclContents);

if (mKbmContents.empty())
mapping = Tunings::startScaleOnAndTuneNoteTo(60, (int)mReferencePitch, mReferenceFreq);
else
mapping = Tunings::parseKBMData(mKbmContents);

auto tuning = Tunings::Tuning(scale, mapping);
for (int i=0; i<256; ++i)
{
mTuningTable[i] = tuning.logScaledFrequencyForMidiNote(i-128);
}
}
catch(const Tunings::TuningError &e)
{
mIntonation = kIntonation_Equal;
ofLog() << e.what();
}
}
}

float Scale::GetTuningTableRatio(int semitonesFromCenter)
Expand Down Expand Up @@ -523,6 +622,71 @@ void Scale::TextEntryComplete(TextEntry* entry)
}
}

void Scale::ButtonClicked(ClickButton *button)
{
if (button == mLoadSCL || button == mLoadKBM)
{
std::string prompt = "Load ";
prompt += (button == mLoadSCL) ? "SCL" : "KBM";
std::string pat = (button == mLoadSCL) ? "*.scl" : "*.kbm";
juce::FileChooser chooser( prompt, juce::File(), pat, true, false, TheSynth->GetMainComponent()->getTopLevelComponent());
if (chooser.browseForFileToOpen())
{
auto file = chooser.getResult();
std::cout << file.getFullPathName().toStdString() << std::endl;
if (button == mLoadSCL)
{
mSclContents = file.loadFileAsString().toStdString();
}
else
{
mKbmContents = file.loadFileAsString().toStdString();
}
UpdateTuningTable();
}
}
}

namespace
{
const int kSaveStateRev = 1;
}

void Scale::SaveState(FileStreamOut &out)
{
IDrawableModule::SaveState(out);

out << kSaveStateRev;

out << mIntonation;
out << mSclContents;
out << mKbmContents;
}

void Scale::LoadState(FileStreamIn &in)
{
IDrawableModule::LoadState(in);

if (!ModuleContainer::DoesModuleHaveMoreSaveData(in))
return; //this was saved before we added versioning, bail out

int rev;
in >> rev;
LoadStateValidate(rev >= kSaveStateRev);

int inton;
in >> inton;
mIntonation = (Scale::IntonationMode)inton;
in >> mSclContents;
in >> mKbmContents;

if (!(mSclContents.empty() && mKbmContents.empty()))
{
ofLog() << "Restoring SCL/KBM from streaming";
UpdateTuningTable();
}
}

void ScalePitches::SetRoot(int root)
{
assert(root >= 0);
Expand Down
27 changes: 23 additions & 4 deletions Source/Scale.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,15 @@ struct ScalePitches
int NumPitchesInScale() const { return (int)mScalePitches.size(); }
};

class Scale : public IDrawableModule, public IDropdownListener, public IFloatSliderListener, public IIntSliderListener, public ITextEntryListener
class MTSClient;

class Scale : public IDrawableModule, public IDropdownListener,
public IFloatSliderListener, public IIntSliderListener, public ITextEntryListener,
public IButtonListener
{
public:
Scale();
~Scale();
void Init() override;

string GetTitleLabel() override { return "scale"; }
Expand Down Expand Up @@ -115,7 +120,7 @@ class Scale : public IDrawableModule, public IDropdownListener, public IFloatSli
string GetScaleName(int index) { return mScales[index].mName; }
int NumPitchesInScale() const { return mScale.NumPitchesInScale(); }
int GetTet() const { return mTet; }

float PitchToFreq(float pitch);
float FreqToPitch(float freq);

Expand All @@ -127,6 +132,11 @@ class Scale : public IDrawableModule, public IDropdownListener, public IFloatSli
void CheckboxUpdated(Checkbox* checkbox) override;
void TextEntryComplete(TextEntry* entry) override;

void ButtonClicked(ClickButton *button) override;

void SaveState(FileStreamOut& out) override;
void LoadState(FileStreamIn& in) override;

private:
struct ScaleInfo
{
Expand All @@ -136,7 +146,7 @@ class Scale : public IDrawableModule, public IDropdownListener, public IFloatSli

//IDrawableModule
void DrawModule() override;
void GetModuleDimensions(float& width, float& height) override { width = 164; height = 62; }
void GetModuleDimensions(float& width, float& height) override { width = 164; height = 82; }
bool Enabled() const override { return true; }

void NotifyListeners();
Expand All @@ -151,7 +161,9 @@ class Scale : public IDrawableModule, public IDropdownListener, public IFloatSli
kIntonation_Just,
kIntonation_Pythagorean,
kIntonation_Meantone,
kIntonation_Rational
kIntonation_Rational,
kIntonation_SCLKBM,
kIntonation_ODDSOUNDMTS
};

ScalePitches mScale;
Expand All @@ -160,6 +172,9 @@ class Scale : public IDrawableModule, public IDropdownListener, public IFloatSli
DropdownList* mScaleSelector;
IntSlider* mScaleDegreeSlider;
int mScaleDegree;

ClickButton* mLoadSCL{nullptr};
ClickButton* mLoadKBM{nullptr};

vector<ScaleInfo> mScales;
int mNumSeptatonicScales;
Expand All @@ -177,6 +192,10 @@ class Scale : public IDrawableModule, public IDropdownListener, public IFloatSli
float mTuningTable[256];

ChordDatabase mChordDatabase;

MTSClient *oddsound_mts_client{nullptr};

std::string mSclContents, mKbmContents;
};

extern Scale* TheScale;
Expand Down
9 changes: 9 additions & 0 deletions libs/oddsound-mts/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
project(oddsound-mts VERSION 0.0.0 LANGUAGES CXX)

add_library(${PROJECT_NAME}
MTS-ESP/Client/libMTSClient.cpp
)

target_include_directories(${PROJECT_NAME} PUBLIC MTS-ESP/Client)
target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_DL_LIBS})
add_library(surge::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
1 change: 1 addition & 0 deletions libs/oddsound-mts/MTS-ESP
Submodule MTS-ESP added at fcfaa5
1 change: 1 addition & 0 deletions libs/tuning-library
Submodule tuning-library added at b30694

0 comments on commit c7f6e18

Please sign in to comment.