Support for alternative tunings and keyboard mappings (#5522)

Co-authored-by: Kevin Zander <veratil@gmail.com>
Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
Co-authored-by: Martin <martin@sigma.he29.net>
This commit is contained in:
Martin Pavelek
2021-09-09 19:49:24 +02:00
committed by GitHub
parent ace502f1a5
commit e07861ced3
42 changed files with 2079 additions and 92 deletions

View File

@@ -25,6 +25,7 @@
#ifndef COMBOBOX_MODEL_H
#define COMBOBOX_MODEL_H
#include <cassert>
#include <memory>
#include <utility>
#include <vector>
@@ -52,6 +53,8 @@ public:
void addItem( QString item, std::unique_ptr<PixmapLoader> loader = nullptr );
void replaceItem(std::size_t index, QString item, std::unique_ptr<PixmapLoader> loader = nullptr);
void clear();
int findText( const QString& txt ) const;

View File

@@ -37,6 +37,7 @@ class BBEditor;
class ControllerRackView;
class FxMixerView;
class MainWindow;
class MicrotunerConfig;
class PianoRollWindow;
class ProjectNotes;
class SongEditorWindow;
@@ -59,6 +60,7 @@ public:
BBEditor* getBBEditor() { return m_bbEditor; }
PianoRollWindow* pianoRoll() { return m_pianoRoll; }
ProjectNotes* getProjectNotes() { return m_projectNotes; }
MicrotunerConfig* getMicrotunerConfig() { return m_microtunerConfig; }
AutomationEditorWindow* automationEditor() { return m_automationEditor; }
ControllerRackView* getControllerRackView() { return m_controllerRackView; }
@@ -78,6 +80,7 @@ private:
BBEditor* m_bbEditor;
PianoRollWindow* m_pianoRoll;
ProjectNotes* m_projectNotes;
MicrotunerConfig* m_microtunerConfig;
ControllerRackView* m_controllerRackView;
QLabel* m_loadingProgressLabel;
};

View File

@@ -65,22 +65,4 @@ private:
} ;
class InstrumentMiscView : public QWidget
{
Q_OBJECT
public:
InstrumentMiscView( InstrumentTrack *it, QWidget* parent );
~InstrumentMiscView();
GroupBox * pitchGroupBox()
{
return m_pitchGroupBox;
}
private:
GroupBox * m_pitchGroupBox;
};
#endif

View File

@@ -0,0 +1,63 @@
/*
* InstrumentMiscView.h - widget in instrument-track-window for setting up
* miscellaneous options not covered by other tabs
*
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2020 Martin Pavelek <he29.HS/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef INSTRUMENT_MISC_VIEW_H
#define INSTRUMENT_MISC_VIEW_H
#include <QWidget>
class ComboBox;
class GroupBox;
class InstrumentTrack;
class LedCheckBox;
class InstrumentMiscView : public QWidget
{
Q_OBJECT
public:
InstrumentMiscView(InstrumentTrack *it, QWidget *parent);
GroupBox *pitchGroupBox() {return m_pitchGroupBox;}
GroupBox *microtunerGroupBox() {return m_microtunerGroupBox;}
ComboBox *scaleCombo() {return m_scaleCombo;}
ComboBox *keymapCombo() {return m_keymapCombo;}
LedCheckBox *rangeImportCheckbox() {return m_rangeImportCheckbox;}
private:
GroupBox *m_pitchGroupBox;
GroupBox *m_microtunerGroupBox;
ComboBox *m_scaleCombo;
ComboBox *m_keymapCombo;
LedCheckBox *m_rangeImportCheckbox;
};
#endif

View File

@@ -30,6 +30,7 @@
#include "GroupBox.h"
#include "InstrumentFunctions.h"
#include "InstrumentSoundShaping.h"
#include "Microtuner.h"
#include "Midi.h"
#include "MidiCCRackView.h"
#include "MidiEventProcessor.h"
@@ -184,15 +185,23 @@ public:
return &m_lastKeyModel;
}
int baseNote() const;
bool keyRangeImport() const;
bool isKeyMapped(int key) const;
int firstKey() const;
int lastKey() const;
int baseNote() const;
float baseFreq() const;
Piano *pianoModel()
{
return &m_piano;
}
Microtuner *microtuner()
{
return &m_microtuner;
}
bool isArpeggioEnabled() const
{
return m_arpeggio.m_arpEnabledModel.value();
@@ -305,6 +314,8 @@ private:
Piano m_piano;
Microtuner m_microtuner;
std::unique_ptr<BoolModel> m_midiCCEnable;
std::unique_ptr<FloatModel> m_midiCCModel[MidiControllerCount];

79
include/Keymap.h Normal file
View File

@@ -0,0 +1,79 @@
/*
* Keymap.h - holds information about a key mapping
*
* Copyright (c) 2020 Martin Pavelek <he29.HS/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef KEYMAP_H
#define KEYMAP_H
#include <vector>
#include <QObject>
#include <QString>
#include "Note.h"
#include "SerializingObject.h"
class Keymap : public QObject, public SerializingObject
{
Q_OBJECT
public:
Keymap();
Keymap(
QString description,
std::vector<int> newMap,
int newFirst,
int newLast,
int newMiddle,
int newBaseKey,
float newBaseFreq
);
QString getDescription() const;
void setDescription(QString description);
int getMiddleKey() const {return m_middleKey;}
int getFirstKey() const {return m_firstKey;}
int getLastKey() const {return m_lastKey;}
int getBaseKey() const {return m_baseKey;}
float getBaseFreq() const {return m_baseFreq;}
std::size_t getSize() const {return m_map.size();}
int getDegree(int key) const;
int getOctave(int key) const;
const std::vector<int> &getMap() const {return m_map;}
void saveSettings(QDomDocument &doc, QDomElement &element) override;
void loadSettings(const QDomElement &element) override;
inline QString nodeName() const override {return "keymap";}
private:
QString m_description; //!< name or description of the keymap
std::vector<int> m_map; //!< key to scale degree mapping
int m_firstKey; //!< first key that will be mapped
int m_lastKey; //!< last key that will be mapped
int m_middleKey; //!< first line of the map refers to this key
int m_baseKey; //!< key which is assigned the reference "base note"
float m_baseFreq; //!< frequency of the base note (usually A4 @440 Hz)
};
#endif

View File

@@ -152,6 +152,7 @@ public slots:
void toggleBBEditorWin( bool forceShow = false );
void toggleSongEditorWin();
void toggleProjectNotesWin();
void toggleMicrotunerWin();
void toggleFxMixerWin();
void togglePianoRollWin();
void toggleControllerRack();

73
include/Microtuner.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* Microtuner.h - manage tuning and scale information of an instrument
*
* Copyright (c) 2020 Martin Pavelek <he29.HS/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef MICROTUNER_H
#define MICROTUNER_H
#include "AutomatableModel.h"
#include "ComboBoxModel.h"
#include "JournallingObject.h"
#include "lmms_constants.h"
#include "Note.h"
class LMMS_EXPORT Microtuner : public Model, public JournallingObject
{
Q_OBJECT
public:
explicit Microtuner();
bool enabled() const {return m_enabledModel.value();}
bool keyRangeImport() const {return enabled() && m_keyRangeImportModel.value();}
int currentScale() const {return m_scaleModel.value();}
int currentKeymap() const {return m_keymapModel.value();}
BoolModel *enabledModel() {return &m_enabledModel;}
ComboBoxModel *scaleModel() {return &m_scaleModel;}
ComboBoxModel *keymapModel() {return &m_keymapModel;}
BoolModel *keyRangeImportModel() {return &m_keyRangeImportModel;}
int firstKey() const;
int lastKey() const;
int baseKey() const;
float baseFreq() const;
float keyToFreq(int key, int userBaseNote) const;
QString nodeName() const override {return "microtuner";}
void saveSettings(QDomDocument & document, QDomElement &element) override;
void loadSettings(const QDomElement &element) override;
protected slots:
void updateScaleList(int index);
void updateKeymapList(int index);
private:
BoolModel m_enabledModel; //!< Enable microtuner (otherwise using 12-TET @440 Hz)
ComboBoxModel m_scaleModel;
ComboBoxModel m_keymapModel;
BoolModel m_keyRangeImportModel;
};
#endif

View File

@@ -0,0 +1,93 @@
/*
* MicrotunerConfig.h - configuration widget for scales and keymaps
*
* Copyright (c) 2020 Martin Pavelek <he29.HS/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef MICROTUNER_CONFIG_H
#define MICROTUNER_CONFIG_H
#include <QCloseEvent>
#include <QLineEdit>
#include <QMainWindow>
#include <QPlainTextEdit>
#include "AutomatableModel.h"
#include "ComboBoxModel.h"
#include "LcdFloatSpinBox.h"
#include "LcdSpinBox.h"
#include "SerializingObject.h"
class LMMS_EXPORT MicrotunerConfig : public QWidget, public SerializingObject
{
Q_OBJECT
public:
MicrotunerConfig();
void saveSettings(QDomDocument &document, QDomElement &element) override;
void loadSettings(const QDomElement &element) override;
inline QString nodeName() const override
{
return "MicrotunerConfig";
}
QSize sizeHint() const override {return QSize(300, 400);}
public slots:
void updateScaleList(int index);
void updateKeymapList(int index);
void updateScaleForm();
void updateKeymapForm();
protected:
void closeEvent(QCloseEvent *ce) override;
private slots:
bool loadScaleFromFile();
bool loadKeymapFromFile();
bool saveScaleToFile();
bool saveKeymapToFile();
private:
bool validateScaleForm();
bool validateKeymapForm();
bool applyScale();
bool applyKeymap();
ComboBoxModel m_scaleComboModel; //!< ID of scale currently selected for editing
ComboBoxModel m_keymapComboModel; //!< ID of keymap currently selected for editing
QLineEdit *m_scaleNameEdit; //!< edit field for the scale name or description
QLineEdit *m_keymapNameEdit; //!< edit field for the keymap name or description
QPlainTextEdit *m_scaleTextEdit; //!< text editor field for interval definitions
QPlainTextEdit *m_keymapTextEdit; //!< text editor field for key mappings
IntModel m_firstKeyModel; //!< model for spinbox of currently edited first key
IntModel m_lastKeyModel; //!< model for spinbox of currently edited last key
IntModel m_middleKeyModel; //!< model for spinbox of currently edited middle key
IntModel m_baseKeyModel; //!< model for spinbox of currently edited base key
FloatModel m_baseFreqModel; //!< model for spinbox of currently edited base note frequency
};
#endif

View File

@@ -55,11 +55,6 @@ const int BYTES_PER_SURROUND_FRAME = sizeof( surroundSampleFrame );
const float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f;
const float BaseFreq = 440.0f;
const Keys BaseKey = Key_A;
const Octaves BaseOctave = DefaultOctave;
#include "PlayHandle.h"

87
include/Scale.h Normal file
View File

@@ -0,0 +1,87 @@
/*
* Scale.h - holds information about a scale and its intervals
*
* Copyright (c) 2020 Martin Pavelek <he29.HS/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef SCALE_H
#define SCALE_H
#include <cmath>
#include <cstdint>
#include <vector>
#include <QObject>
#include <QString>
#include "SerializingObject.h"
class Interval : public SerializingObject
{
public:
Interval() : m_numerator(1), m_denominator(1), m_cents(0), m_ratio(1) {};
explicit Interval(float cents);
Interval(uint32_t numerator, uint32_t denominator);
float getRatio() const {return m_ratio;}
QString getString() const
{
if (m_denominator) {return QString::number(m_numerator) + "/" + QString::number(m_denominator);}
else {return QString().sprintf("%.4f", m_cents);}
}
void saveSettings(QDomDocument &doc, QDomElement &element) override;
void loadSettings(const QDomElement &element) override;
inline QString nodeName() const override {return "interval";}
private:
// Scala specifies that numerators and denominators should go at least up to 2147483647 → use uint32_t.
uint32_t m_numerator; //!< numerator of the interval fraction
uint32_t m_denominator; //!< denominator of the interval fraction
float m_cents; //!< interval defined in cents (used when denominator is set to zero)
float m_ratio; //!< precomputed output value for better performance
};
class Scale : public QObject, public SerializingObject
{
Q_OBJECT
public:
Scale();
Scale(QString description, std::vector<Interval> intervals);
QString getDescription() const;
void setDescription(QString description);
const std::vector<Interval> &getIntervals() const {return m_intervals;}
void setIntervals(std::vector<Interval> input) {m_intervals = std::move(input);}
void saveSettings(QDomDocument &doc, QDomElement &element) override;
void loadSettings(const QDomElement &element) override;
inline QString nodeName() const override {return "scale";}
private:
QString m_description; //!< name or description of the scale
std::vector<Interval> m_intervals; //!< a series of ratios that define the scale
};
#endif

View File

@@ -25,6 +25,7 @@
#ifndef SONG_H
#define SONG_H
#include <memory>
#include <utility>
#include <QtCore/QSharedMemory>
@@ -34,8 +35,11 @@
#include "TrackContainer.h"
#include "Controller.h"
#include "Keymap.h"
#include "lmms_constants.h"
#include "MeterModel.h"
#include "Mixer.h"
#include "Scale.h"
#include "VstSyncController.h"
@@ -350,6 +354,11 @@ public:
bool isSavingProject() const;
std::shared_ptr<const Scale> getScale(unsigned int index) const;
std::shared_ptr<const Keymap> getKeymap(unsigned int index) const;
void setScale(unsigned int index, std::shared_ptr<Scale> newScale);
void setKeymap(unsigned int index, std::shared_ptr<Keymap> newMap);
public slots:
void playSong();
void record();
@@ -416,6 +425,12 @@ private:
void removeAllControllers();
void saveScaleStates(QDomDocument &doc, QDomElement &element);
void restoreScaleStates(const QDomElement &element);
void saveKeymapStates(QDomDocument &doc, QDomElement &element);
void restoreKeymapStates(const QDomElement &element);
void processAutomations(const TrackList& tracks, TimePos timeStart, fpp_t frames);
void setModified(bool value);
@@ -475,6 +490,9 @@ private:
TimePos m_exportSongEnd;
TimePos m_exportEffectiveLength;
std::shared_ptr<Scale> m_scales[MaxScaleCount];
std::shared_ptr<Keymap> m_keymaps[MaxKeymapCount];
AutomatedValueMap m_oldAutomatedValues;
friend class LmmsCore;
@@ -495,6 +513,8 @@ signals:
void stopped();
void modified();
void projectFileNameChanged();
void scaleListChanged(int index);
void keymapListChanged(int index);
} ;

View File

@@ -49,9 +49,13 @@ const float F_PI_SQR = (float) LD_PI_SQR;
const float F_E = (float) LD_E;
const float F_E_R = (float) LD_E_R;
// Microtuner
const unsigned int MaxScaleCount = 10; //!< number of scales per project
const unsigned int MaxKeymapCount = 10; //!< number of keyboard mappings per project
// Frequency ranges (in Hz).
// Arbitrary low limit for logarithmic frequency scale; >1 Hz.
const int LOWEST_LOG_FREQ = 10;
const int LOWEST_LOG_FREQ = 5;
// Full range is defined by LOWEST_LOG_FREQ and current sample rate.
enum FREQUENCY_RANGES