MIDI range MKII (extracted from microtonal PR) (#5868)
* Update MIDI range to match MIDI specification Co-authored-by: IanCaio <iancaio_dev@hotmail.com> Co-authored-by: Kevin Zander <veratil@gmail.com> Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com> Co-authored-by: Spekular <Spekular@users.noreply.github.com>
This commit is contained in:
BIN
data/themes/classic/black_key_disabled.png
Normal file
BIN
data/themes/classic/black_key_disabled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -165,9 +165,11 @@ PianoRoll {
|
||||
qproperty-whiteKeyInactiveTextColor: rgb( 128, 128, 128);
|
||||
qproperty-whiteKeyInactiveTextShadow: rgb( 240, 240, 240 );
|
||||
qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff);
|
||||
qproperty-whiteKeyDisabledBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #a0a0a0, stop:1 #b0b0b0);
|
||||
qproperty-blackKeyWidth: 48;
|
||||
qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c);
|
||||
qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000);
|
||||
qproperty-blackKeyDisabledBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #606060, stop:1 #505050);
|
||||
/* Grid colors */
|
||||
qproperty-lineColor: rgba( 128, 128, 128, 80 );
|
||||
qproperty-beatLineColor: rgba( 128, 128, 128, 160 );
|
||||
|
||||
BIN
data/themes/classic/white_key_disabled.png
Normal file
BIN
data/themes/classic/white_key_disabled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
data/themes/default/black_key_disabled.png
Normal file
BIN
data/themes/default/black_key_disabled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -197,9 +197,11 @@ PianoRoll {
|
||||
qproperty-whiteKeyInactiveTextColor: #000;
|
||||
qproperty-whiteKeyInactiveTextShadow: #fff;
|
||||
qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff);
|
||||
qproperty-whiteKeyDisabledBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #a0a0a0, stop:1 #b0b0b0);
|
||||
qproperty-blackKeyWidth: 48;
|
||||
qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c);
|
||||
qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000);
|
||||
qproperty-blackKeyDisabledBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #606060, stop:1 #505050);
|
||||
/* Grid colors */
|
||||
qproperty-lineColor: #292929;
|
||||
qproperty-beatLineColor: #2d6b45;
|
||||
|
||||
BIN
data/themes/default/white_key_disabled.png
Normal file
BIN
data/themes/default/white_key_disabled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -120,6 +120,7 @@ private:
|
||||
void upgrade_1_3_0();
|
||||
void upgrade_noHiddenClipNames();
|
||||
void upgrade_automationNodes();
|
||||
void upgrade_extendedNoteRange();
|
||||
|
||||
// List of all upgrade methods
|
||||
static const std::vector<UpgradeMethod> UPGRADE_METHODS;
|
||||
|
||||
@@ -174,7 +174,19 @@ public:
|
||||
return &m_baseNoteModel;
|
||||
}
|
||||
|
||||
IntModel *firstKeyModel()
|
||||
{
|
||||
return &m_firstKeyModel;
|
||||
}
|
||||
|
||||
IntModel *lastKeyModel()
|
||||
{
|
||||
return &m_lastKeyModel;
|
||||
}
|
||||
|
||||
int baseNote() const;
|
||||
int firstKey() const;
|
||||
int lastKey() const;
|
||||
|
||||
Piano *pianoModel()
|
||||
{
|
||||
@@ -265,11 +277,13 @@ private:
|
||||
|
||||
bool m_previewMode;
|
||||
|
||||
IntModel m_baseNoteModel; //!< The "A4" or "440 Hz" key (default 69)
|
||||
IntModel m_firstKeyModel; //!< First key the instrument reacts to
|
||||
IntModel m_lastKeyModel; //!< Last key the instrument reacts to
|
||||
|
||||
bool m_hasAutoMidiDev;
|
||||
static InstrumentTrack *s_autoAssignedTrack;
|
||||
|
||||
IntModel m_baseNoteModel;
|
||||
|
||||
NotePlayHandleList m_processHandles;
|
||||
|
||||
FloatModel m_volumeModel;
|
||||
@@ -282,7 +296,6 @@ private:
|
||||
IntModel m_effectChannelModel;
|
||||
BoolModel m_useMasterPitchModel;
|
||||
|
||||
|
||||
Instrument * m_instrument;
|
||||
InstrumentSoundShaping m_soundShaping;
|
||||
InstrumentFunctionArpeggio m_arpeggio;
|
||||
|
||||
@@ -55,6 +55,7 @@ enum Keys
|
||||
|
||||
enum Octaves
|
||||
{
|
||||
Octave_m1, // MIDI standard starts at C-1
|
||||
Octave_0,
|
||||
Octave_1,
|
||||
Octave_2,
|
||||
@@ -64,15 +65,19 @@ enum Octaves
|
||||
Octave_6,
|
||||
Octave_7,
|
||||
Octave_8,
|
||||
Octave_9, // incomplete octave, MIDI only goes up to G9
|
||||
NumOctaves
|
||||
} ;
|
||||
};
|
||||
|
||||
const int FirstOctave = -1;
|
||||
const int KeysPerOctave = 12;
|
||||
const int DefaultKey = DefaultOctave * KeysPerOctave + Key_A;
|
||||
//! Number of physical keys, limited to MIDI range (valid for both MIDI 1.0 and 2.0)
|
||||
const int NumKeys = 128;
|
||||
|
||||
const int WhiteKeysPerOctave = 7;
|
||||
const int BlackKeysPerOctave = 5;
|
||||
const int KeysPerOctave = WhiteKeysPerOctave + BlackKeysPerOctave;
|
||||
const int NumKeys = NumOctaves * KeysPerOctave;
|
||||
const int DefaultKey = DefaultOctave*KeysPerOctave + Key_A;
|
||||
const int DefaultMiddleKey = Octave_4 * KeysPerOctave + Key_C;
|
||||
const int DefaultBaseKey = Octave_4 * KeysPerOctave + Key_A;
|
||||
const float DefaultBaseFreq = 440.f;
|
||||
|
||||
const float MaxDetuning = 4 * 12.0f;
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
/*! Returns whether the play handle plays on a certain track */
|
||||
bool isFromTrack( const Track* _track ) const override;
|
||||
|
||||
/*! Releases the note (and plays release frames */
|
||||
/*! Releases the note (and plays release frames) */
|
||||
void noteOff( const f_cnt_t offset = 0 );
|
||||
|
||||
/*! Returns number of frames to be played until the note is going to be released */
|
||||
|
||||
@@ -65,6 +65,10 @@ public:
|
||||
static bool isWhiteKey(int key);
|
||||
static bool isBlackKey(int key);
|
||||
|
||||
static const unsigned int WhiteKeysPerOctave = 7;
|
||||
static const unsigned int BlackKeysPerOctave = 5;
|
||||
static const unsigned int NumWhiteKeys = 75;
|
||||
static const unsigned int NumBlackKeys = 53;
|
||||
|
||||
private:
|
||||
static bool isValidKey( int key )
|
||||
|
||||
@@ -86,10 +86,12 @@ class PianoRoll : public QWidget
|
||||
Q_PROPERTY(QColor whiteKeyActiveTextColor MEMBER m_whiteKeyActiveTextColor)
|
||||
Q_PROPERTY(QColor whiteKeyActiveTextShadow MEMBER m_whiteKeyActiveTextShadow)
|
||||
Q_PROPERTY(QBrush whiteKeyActiveBackground MEMBER m_whiteKeyActiveBackground)
|
||||
Q_PROPERTY(QBrush whiteKeyDisabledBackground MEMBER m_whiteKeyDisabledBackground)
|
||||
/* black key properties */
|
||||
Q_PROPERTY(int blackKeyWidth MEMBER m_blackKeyWidth)
|
||||
Q_PROPERTY(QBrush blackKeyInactiveBackground MEMBER m_blackKeyInactiveBackground)
|
||||
Q_PROPERTY(QBrush blackKeyActiveBackground MEMBER m_blackKeyActiveBackground)
|
||||
Q_PROPERTY(QBrush blackKeyDisabledBackground MEMBER m_blackKeyDisabledBackground)
|
||||
public:
|
||||
enum EditModes
|
||||
{
|
||||
@@ -398,7 +400,6 @@ private:
|
||||
int m_pianoKeysVisible;
|
||||
|
||||
int m_keyLineHeight;
|
||||
int m_octaveHeight;
|
||||
int m_whiteKeySmallHeight;
|
||||
int m_whiteKeyBigHeight;
|
||||
int m_blackKeyHeight;
|
||||
@@ -475,10 +476,12 @@ private:
|
||||
QColor m_whiteKeyInactiveTextColor;
|
||||
QColor m_whiteKeyInactiveTextShadow;
|
||||
QBrush m_whiteKeyInactiveBackground;
|
||||
QBrush m_whiteKeyDisabledBackground;
|
||||
/* black key properties */
|
||||
int m_blackKeyWidth;
|
||||
QBrush m_blackKeyActiveBackground;
|
||||
QBrush m_blackKeyInactiveBackground;
|
||||
QBrush m_blackKeyDisabledBackground;
|
||||
|
||||
signals:
|
||||
void positionChanged( const TimePos & );
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <QPixmap>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "AutomatableModel.h"
|
||||
#include "ModelView.h"
|
||||
|
||||
class Piano;
|
||||
@@ -63,17 +64,24 @@ protected:
|
||||
private:
|
||||
int getKeyFromMouse( const QPoint & _p ) const;
|
||||
int getKeyX( int _key_num ) const;
|
||||
int getKeyWidth(int key_num) const;
|
||||
int getKeyHeight(int key_num) const;
|
||||
IntModel *getNearestMarker(int key, QString* title = nullptr);
|
||||
|
||||
static QPixmap * s_whiteKeyPm;
|
||||
static QPixmap * s_blackKeyPm;
|
||||
static QPixmap * s_whiteKeyPressedPm;
|
||||
static QPixmap * s_blackKeyPressedPm;
|
||||
static QPixmap * s_whiteKeyDisabledPm;
|
||||
static QPixmap * s_blackKeyDisabledPm;
|
||||
|
||||
Piano * m_piano;
|
||||
|
||||
QScrollBar * m_pianoScroll;
|
||||
int m_startKey; // first key when drawing
|
||||
int m_lastKey;
|
||||
int m_startKey; //!< first key when drawing
|
||||
int m_lastKey; //!< previously pressed key
|
||||
IntModel *m_movedNoteModel; //!< note marker which is being moved
|
||||
|
||||
|
||||
|
||||
private slots:
|
||||
@@ -87,4 +95,3 @@ signals:
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -122,7 +122,9 @@ public:
|
||||
else if ( sKey == "B" ) {
|
||||
m_key = NoteKey::B;
|
||||
}
|
||||
return m_key + (nOctave*12)+57;
|
||||
|
||||
// Hydrogen records MIDI notes from C-1 to B5, and exports them as a number ranging from -3 to 3
|
||||
return m_key + ((nOctave + 3) * 12);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -130,8 +130,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks,
|
||||
if (n.nodeName() == "instrumenttrack")
|
||||
{
|
||||
QDomElement it = n.toElement();
|
||||
// transpose +12 semitones, workaround for #1857
|
||||
base_pitch = (69 - it.attribute("basenote", "57").toInt());
|
||||
base_pitch = (69 - it.attribute("basenote", "69").toInt());
|
||||
if (it.attribute("usemasterpitch", "1").toInt())
|
||||
{
|
||||
base_pitch += masterPitch;
|
||||
@@ -201,8 +200,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks,
|
||||
if (n.nodeName() == "instrumenttrack")
|
||||
{
|
||||
QDomElement it = n.toElement();
|
||||
// transpose +12 semitones, workaround for #1857
|
||||
base_pitch = (69 - it.attribute("basenote", "57").toInt());
|
||||
base_pitch = (69 - it.attribute("basenote", "69").toInt());
|
||||
if (it.attribute("usemasterpitch", "1").toInt())
|
||||
{
|
||||
base_pitch += masterPitch;
|
||||
|
||||
@@ -454,7 +454,7 @@ bool MidiImport::readSMF( TrackContainer* tc )
|
||||
int ticks = noteEvt->get_duration() * ticksPerBeat;
|
||||
Note n( (ticks < 1 ? 1 : ticks ),
|
||||
noteEvt->get_start_time() * ticksPerBeat,
|
||||
noteEvt->get_identifier() - 12,
|
||||
noteEvt->get_identifier(),
|
||||
noteEvt->get_loud() * (200.f / 127.f)); // Map from MIDI velocity to LMMS volume
|
||||
ch->addNote( n );
|
||||
|
||||
|
||||
@@ -300,8 +300,7 @@ bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const TimePos&
|
||||
|
||||
switch(event.type()) {
|
||||
case MidiNoteOn:
|
||||
// to get us in line with MIDI(?)
|
||||
key = event.key() +12;
|
||||
key = event.key();
|
||||
vel = event.velocity();
|
||||
|
||||
voice = popVoice();
|
||||
@@ -316,7 +315,7 @@ bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const TimePos&
|
||||
}
|
||||
break;
|
||||
case MidiNoteOff:
|
||||
key = event.key() +12;
|
||||
key = event.key();
|
||||
for(voice=0; voice<OPL2_VOICES; ++voice) {
|
||||
if( voiceNote[voice] == key ) {
|
||||
theEmulator->write(0xA0+voice, fnums[key] & 0xff);
|
||||
@@ -328,7 +327,7 @@ bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const TimePos&
|
||||
velocities[key] = 0;
|
||||
break;
|
||||
case MidiKeyPressure:
|
||||
key = event.key() +12;
|
||||
key = event.key();
|
||||
vel = event.velocity();
|
||||
if( velocities[key] != 0) {
|
||||
velocities[key] = vel;
|
||||
|
||||
@@ -824,8 +824,8 @@ QString XpressiveHelpView::s_helpText=
|
||||
"<h4>Available variables:</h4><br>"
|
||||
"<b>t</b> - Time in seconds.<br>"
|
||||
"<b>f</b> - Note's pitched frequency. Available only in the output expressions.<br>"
|
||||
"<b>key</b> - Note's keyboard key. 0 denotes C0, 48 denotes C4, 96 denotes C8. Available only in the output expressions.<br>"
|
||||
"<b>bnote</b> - Base note. By default it is 57 which means A5, unless you change it.<br>"
|
||||
"<b>key</b> - Note's keyboard key. 0 denotes C-1, 60 denotes C4, 127 denotes G9. Available only in the output expressions.<br>"
|
||||
"<b>bnote</b> - Base note. By default it is 69 which means A4, unless you change it.<br>"
|
||||
"<b>srate</b> - Sample rate. In wave expression it returns the wave's number of samples.<br>"
|
||||
"<b>tempo</b> - Song's Tempo. Available only in the output expressions.<br>"
|
||||
"<b>v</b> - Note's volume. Note that the output is already multiplied by the volume. Available only in the output expressions.<br>"
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
#include "Note.h"
|
||||
#include "CustomTextKnob.h"
|
||||
#include "SubWindow.h"
|
||||
#include "AutomatableModel.h"
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
#define BUILD_REMOTE_PLUGIN_CLIENT
|
||||
|
||||
#include "Note.h"
|
||||
#include "RemotePlugin.h"
|
||||
#include "RemoteZynAddSubFx.h"
|
||||
#include "LocalZynAddSubFx.h"
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "AutomationTrack.h"
|
||||
#include "BBTrackContainer.h"
|
||||
#include "LocaleHelper.h"
|
||||
#include "Note.h"
|
||||
#include "ProjectJournal.h"
|
||||
#include "Song.h"
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ const std::vector<DataFile::UpgradeMethod> DataFile::UPGRADE_METHODS = {
|
||||
&DataFile::upgrade_1_0_99 , &DataFile::upgrade_1_1_0,
|
||||
&DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3,
|
||||
&DataFile::upgrade_1_3_0 , &DataFile::upgrade_noHiddenClipNames,
|
||||
&DataFile::upgrade_automationNodes
|
||||
&DataFile::upgrade_automationNodes , &DataFile::upgrade_extendedNoteRange
|
||||
};
|
||||
|
||||
// Vector of all versions that have upgrade routines.
|
||||
@@ -1656,6 +1656,84 @@ void DataFile::upgrade_automationNodes()
|
||||
}
|
||||
|
||||
|
||||
/** \brief Note range has been extended to match MIDI specification
|
||||
*
|
||||
* The non-standard note range previously affected all MIDI-based instruments
|
||||
* except OpulenZ, and made them sound an octave lower than they should (#1857).
|
||||
*/
|
||||
void DataFile::upgrade_extendedNoteRange()
|
||||
{
|
||||
auto affected = [](const QDomElement& instrument)
|
||||
{
|
||||
return instrument.attribute("name") == "zynaddsubfx" ||
|
||||
instrument.attribute("name") == "vestige" ||
|
||||
instrument.attribute("name") == "lv2instrument" ||
|
||||
instrument.attribute("name") == "carlapatchbay" ||
|
||||
instrument.attribute("name") == "carlarack";
|
||||
};
|
||||
|
||||
if (!elementsByTagName("song").item(0).isNull())
|
||||
{
|
||||
// Dealing with a project file, go through all the tracks
|
||||
QDomNodeList tracks = elementsByTagName("track");
|
||||
for (int i = 0; !tracks.item(i).isNull(); i++)
|
||||
{
|
||||
// Ignore BB container tracks
|
||||
if (tracks.item(i).toElement().attribute("type").toInt() == 1) { continue; }
|
||||
|
||||
QDomNodeList instruments = tracks.item(i).toElement().elementsByTagName("instrument");
|
||||
if (instruments.isEmpty()) { continue; }
|
||||
QDomElement instrument = instruments.item(0).toElement();
|
||||
// Raise the base note of every instrument by 12 to compensate for the change
|
||||
// of A4 key code from 57 to 69. This ensures that notes are labeled correctly.
|
||||
instrument.parentNode().toElement().setAttribute(
|
||||
"basenote",
|
||||
instrument.parentNode().toElement().attribute("basenote").toInt() + 12);
|
||||
// Raise the pitch of all notes in patterns assigned to instruments not affected
|
||||
// by #1857 by an octave. This negates the base note change for normal instruments,
|
||||
// but leaves the MIDI-based instruments sounding an octave lower, preserving their
|
||||
// pitch in existing projects.
|
||||
if (!affected(instrument))
|
||||
{
|
||||
QDomNodeList patterns = tracks.item(i).toElement().elementsByTagName("pattern");
|
||||
for (int i = 0; !patterns.item(i).isNull(); i++)
|
||||
{
|
||||
QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note");
|
||||
for (int i = 0; !notes.item(i).isNull(); i++)
|
||||
{
|
||||
notes.item(i).toElement().setAttribute(
|
||||
"key",
|
||||
notes.item(i).toElement().attribute("key").toInt() + 12
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dealing with a preset, not a song
|
||||
QDomNodeList presets = elementsByTagName("instrumenttrack");
|
||||
if (presets.item(0).isNull()) { return; }
|
||||
QDomElement preset = presets.item(0).toElement();
|
||||
// Common correction for all instrument presets (make base notes match the new MIDI range).
|
||||
// NOTE: Many older presets do not have any basenote defined, assume they were "made for 57".
|
||||
// (Specifying a default like this also happens to fix a FileBrowser bug where previews of presets
|
||||
// with undefined basenote always play with the basenote inherited from previously played preview.)
|
||||
int oldBase = preset.attribute("basenote", "57").toInt();
|
||||
preset.setAttribute("basenote", oldBase + 12);
|
||||
// Extra correction for Zyn, VeSTige, LV2 and Carla (to preserve the original buggy behavior).
|
||||
QDomNodeList instruments = presets.item(0).toElement().elementsByTagName("instrument");
|
||||
if (instruments.isEmpty()) { return; }
|
||||
QDomElement instrument = instruments.item(0).toElement();
|
||||
if (affected(instrument))
|
||||
{
|
||||
preset.setAttribute("basenote", preset.attribute("basenote").toInt() + 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DataFile::upgrade()
|
||||
{
|
||||
// Runs all necessary upgrade methods
|
||||
|
||||
@@ -182,7 +182,7 @@ int NotePlayHandle::midiKey() const
|
||||
|
||||
void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
{
|
||||
if( m_muted )
|
||||
if (m_muted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -196,6 +196,22 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
|
||||
lock();
|
||||
|
||||
// Don't play the note if it falls outside of the user defined key range
|
||||
// TODO: handle the range check by Microtuner, and if the key becomes "not mapped", save the current frequency
|
||||
// so that the note release can finish playing using a valid frequency instead of a 1 Hz placeholder
|
||||
if (key() < m_instrumentTrack->m_firstKeyModel.value() ||
|
||||
key() > m_instrumentTrack->m_lastKeyModel.value())
|
||||
{
|
||||
// Release the note in case it started playing before going out of range
|
||||
noteOff(0);
|
||||
// Exit if the note did not start playing before going out of range (i.e. there is no need to play release)
|
||||
if (m_totalFramesPlayed == 0)
|
||||
{
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* It is possible for NotePlayHandle::noteOff to be called before NotePlayHandle::play,
|
||||
* which results in a note-on message being sent without a subsequent note-off message.
|
||||
* Therefore, we check here whether the note has already been released before sending
|
||||
|
||||
@@ -176,21 +176,21 @@ void MidiAlsaSeq::processOutEvent( const MidiEvent& event, const TimePos& time,
|
||||
case MidiNoteOn:
|
||||
snd_seq_ev_set_noteon( &ev,
|
||||
event.channel(),
|
||||
event.key() + KeysPerOctave,
|
||||
event.key(),
|
||||
event.velocity() );
|
||||
break;
|
||||
|
||||
case MidiNoteOff:
|
||||
snd_seq_ev_set_noteoff( &ev,
|
||||
event.channel(),
|
||||
event.key() + KeysPerOctave,
|
||||
event.key(),
|
||||
event.velocity() );
|
||||
break;
|
||||
|
||||
case MidiKeyPressure:
|
||||
snd_seq_ev_set_keypress( &ev,
|
||||
event.channel(),
|
||||
event.key() + KeysPerOctave,
|
||||
event.key(),
|
||||
event.velocity() );
|
||||
break;
|
||||
|
||||
@@ -531,8 +531,7 @@ void MidiAlsaSeq::run()
|
||||
case SND_SEQ_EVENT_NOTEON:
|
||||
dest->processInEvent( MidiEvent( MidiNoteOn,
|
||||
ev->data.note.channel,
|
||||
ev->data.note.note -
|
||||
KeysPerOctave,
|
||||
ev->data.note.note,
|
||||
ev->data.note.velocity,
|
||||
source
|
||||
),
|
||||
@@ -542,8 +541,7 @@ void MidiAlsaSeq::run()
|
||||
case SND_SEQ_EVENT_NOTEOFF:
|
||||
dest->processInEvent( MidiEvent( MidiNoteOff,
|
||||
ev->data.note.channel,
|
||||
ev->data.note.note -
|
||||
KeysPerOctave,
|
||||
ev->data.note.note,
|
||||
ev->data.note.velocity,
|
||||
source
|
||||
),
|
||||
@@ -554,8 +552,7 @@ void MidiAlsaSeq::run()
|
||||
dest->processInEvent( MidiEvent(
|
||||
MidiKeyPressure,
|
||||
ev->data.note.channel,
|
||||
ev->data.note.note -
|
||||
KeysPerOctave,
|
||||
ev->data.note.note,
|
||||
ev->data.note.velocity,
|
||||
source
|
||||
), TimePos() );
|
||||
|
||||
@@ -318,28 +318,28 @@ void MidiApple::HandleReadCallback( const MIDIPacketList *pktlist, void *srcConn
|
||||
}
|
||||
|
||||
unsigned char messageChannel = status & 0xF;
|
||||
const MidiEventTypes cmdtype = static_cast<MidiEventTypes>( status & 0xF0 );
|
||||
const MidiEventTypes cmdtype = static_cast<MidiEventTypes>(status & 0xF0);
|
||||
const int par1 = packet->data[iByte + 1];
|
||||
const int par2 = packet->data[iByte + 2];
|
||||
|
||||
switch (cmdtype)
|
||||
{
|
||||
case MidiNoteOff: //0x80:
|
||||
case MidiNoteOn: //0x90:
|
||||
case MidiKeyPressure: //0xA0:
|
||||
notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1 - KeysPerOctave, par2 & 0xff, &endPointRef ));
|
||||
case MidiNoteOff: //0x80:
|
||||
case MidiNoteOn: //0x90:
|
||||
case MidiKeyPressure: //0xA0:
|
||||
case MidiControlChange: //0xB0:
|
||||
case MidiProgramChange: //0xC0:
|
||||
case MidiChannelPressure: //0xD0:
|
||||
notifyMidiPortList(
|
||||
m_inputSubs[refName],
|
||||
MidiEvent(cmdtype, messageChannel, par1, par2 & 0xff, &endPointRef));
|
||||
break;
|
||||
|
||||
case MidiControlChange: //0xB0:
|
||||
case MidiProgramChange: //0xC0:
|
||||
case MidiChannelPressure: //0xD0:
|
||||
notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1, par2 & 0xff, &endPointRef ));
|
||||
case MidiPitchBend: //0xE0:
|
||||
notifyMidiPortList(
|
||||
m_inputSubs[refName],
|
||||
MidiEvent(cmdtype, messageChannel, par1 + par2 * 128, 0, &endPointRef));
|
||||
break;
|
||||
|
||||
case MidiPitchBend: //0xE0:
|
||||
notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1 + par2 * 128, 0, &endPointRef ));
|
||||
break;
|
||||
case MidiActiveSensing: //0xF0
|
||||
case MidiActiveSensing: //0xF0
|
||||
case 0xF0:
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -214,34 +214,30 @@ void MidiClientRaw::parseData( const unsigned char c )
|
||||
* We simply keep the status as it is, just reset the parameter counter.
|
||||
* If another status byte comes in, it will overwrite the status.
|
||||
*/
|
||||
m_midiParseData.m_midiEvent.setType( static_cast<MidiEventTypes>( m_midiParseData.m_status ) );
|
||||
m_midiParseData.m_midiEvent.setChannel( m_midiParseData.m_channel );
|
||||
m_midiParseData.m_midiEvent.setType(static_cast<MidiEventTypes>(m_midiParseData.m_status));
|
||||
m_midiParseData.m_midiEvent.setChannel(m_midiParseData.m_channel);
|
||||
m_midiParseData.m_bytes = 0; /* Related to running status! */
|
||||
switch( m_midiParseData.m_midiEvent.type() )
|
||||
switch (m_midiParseData.m_midiEvent.type())
|
||||
{
|
||||
case MidiNoteOff:
|
||||
case MidiNoteOn:
|
||||
case MidiKeyPressure:
|
||||
case MidiChannelPressure:
|
||||
m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] - KeysPerOctave );
|
||||
m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] );
|
||||
break;
|
||||
|
||||
case MidiProgramChange:
|
||||
m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] );
|
||||
m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] );
|
||||
m_midiParseData.m_midiEvent.setKey(m_midiParseData.m_buffer[0]);
|
||||
m_midiParseData.m_midiEvent.setVelocity(m_midiParseData.m_buffer[1]);
|
||||
break;
|
||||
|
||||
case MidiControlChange:
|
||||
m_midiParseData.m_midiEvent.setControllerNumber( m_midiParseData.m_buffer[0] );
|
||||
m_midiParseData.m_midiEvent.setControllerValue( m_midiParseData.m_buffer[1] );
|
||||
m_midiParseData.m_midiEvent.setControllerNumber(m_midiParseData.m_buffer[0]);
|
||||
m_midiParseData.m_midiEvent.setControllerValue( m_midiParseData.m_buffer[1]);
|
||||
break;
|
||||
|
||||
case MidiPitchBend:
|
||||
// Pitch-bend is transmitted with 14-bit precision.
|
||||
// Note: '|' does here the same as '+' (no common bits),
|
||||
// but might be faster
|
||||
m_midiParseData.m_midiEvent.setPitchBend( ( m_midiParseData.m_buffer[1] * 128 ) | m_midiParseData.m_buffer[0] );
|
||||
m_midiParseData.m_midiEvent.setPitchBend((m_midiParseData.m_buffer[1] * 128) | m_midiParseData.m_buffer[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -266,22 +262,21 @@ void MidiClientRaw::processParsedEvent()
|
||||
|
||||
|
||||
|
||||
void MidiClientRaw::processOutEvent( const MidiEvent& event, const TimePos & , const MidiPort* port )
|
||||
void MidiClientRaw::processOutEvent(const MidiEvent& event, const TimePos&, const MidiPort* port)
|
||||
{
|
||||
// TODO: also evaluate _time and queue event if necessary
|
||||
switch( event.type() )
|
||||
switch (event.type())
|
||||
{
|
||||
case MidiNoteOn:
|
||||
case MidiNoteOff:
|
||||
case MidiKeyPressure:
|
||||
sendByte( event.type() | event.channel() );
|
||||
sendByte( event.key() + KeysPerOctave );
|
||||
sendByte( event.velocity() );
|
||||
sendByte(event.type() | event.channel());
|
||||
sendByte(event.key());
|
||||
sendByte(event.velocity());
|
||||
break;
|
||||
|
||||
default:
|
||||
qWarning( "MidiClientRaw: unhandled MIDI-event %d\n",
|
||||
(int) event.type() );
|
||||
qWarning("MidiClientRaw: unhandled MIDI-event %d\n", (int)event.type());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -330,5 +325,3 @@ int MidiClientRaw::eventLength( const unsigned char event )
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -201,28 +201,25 @@ void MidiWinMM::handleInputEvent( HMIDIIN hm, DWORD ev )
|
||||
}
|
||||
|
||||
const MidiPortList & l = m_inputSubs[d];
|
||||
for( MidiPortList::ConstIterator it = l.begin(); it != l.end(); ++it )
|
||||
for (MidiPortList::ConstIterator it = l.begin(); it != l.end(); ++it)
|
||||
{
|
||||
switch( cmdtype )
|
||||
switch (cmdtype)
|
||||
{
|
||||
case MidiNoteOn:
|
||||
case MidiNoteOff:
|
||||
case MidiKeyPressure:
|
||||
( *it )->processInEvent( MidiEvent( cmdtype, chan, par1 - KeysPerOctave, par2 & 0xff, &hm ) );
|
||||
break;
|
||||
|
||||
case MidiControlChange:
|
||||
case MidiProgramChange:
|
||||
case MidiChannelPressure:
|
||||
( *it )->processInEvent( MidiEvent( cmdtype, chan, par1, par2 & 0xff, &hm ) );
|
||||
(*it)->processInEvent(MidiEvent(cmdtype, chan, par1, par2 & 0xff, &hm));
|
||||
break;
|
||||
|
||||
case MidiPitchBend:
|
||||
( *it )->processInEvent( MidiEvent( cmdtype, chan, par1 + par2*128, 0, &hm ) );
|
||||
(*it)->processInEvent(MidiEvent(cmdtype, chan, par1 + par2 * 128, 0, &hm));
|
||||
break;
|
||||
|
||||
default:
|
||||
qWarning( "MidiWinMM: unhandled input event %d\n", cmdtype );
|
||||
qWarning("MidiWinMM: unhandled input event %d\n", cmdtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -304,4 +301,3 @@ void MidiWinMM::openDevices()
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -41,15 +41,18 @@
|
||||
#include <QCursor>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "PianoView.h"
|
||||
#include "Piano.h"
|
||||
#include "CaptionMenu.h"
|
||||
#include "embed.h"
|
||||
#include "Engine.h"
|
||||
#include "gui_templates.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "Knob.h"
|
||||
#include "Song.h"
|
||||
#include "StringPairDrag.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
@@ -62,10 +65,12 @@ Keys WhiteKeys[] =
|
||||
} ;
|
||||
|
||||
|
||||
QPixmap * PianoView::s_whiteKeyPm = NULL; /*!< A white key released */
|
||||
QPixmap * PianoView::s_blackKeyPm = NULL; /*!< A black key released */
|
||||
QPixmap * PianoView::s_whiteKeyPressedPm = NULL; /*!< A white key pressed */
|
||||
QPixmap * PianoView::s_blackKeyPressedPm = NULL; /*!< A black key pressed */
|
||||
QPixmap * PianoView::s_whiteKeyPm = nullptr; /*!< A white key released */
|
||||
QPixmap * PianoView::s_blackKeyPm = nullptr; /*!< A black key released */
|
||||
QPixmap * PianoView::s_whiteKeyPressedPm = nullptr; /*!< A white key pressed */
|
||||
QPixmap * PianoView::s_blackKeyPressedPm = nullptr; /*!< A black key pressed */
|
||||
QPixmap * PianoView::s_whiteKeyDisabledPm = nullptr; /*!< A white key disabled */
|
||||
QPixmap * PianoView::s_blackKeyDisabledPm = nullptr; /*!< A black key disabled */
|
||||
|
||||
|
||||
const int PIANO_BASE = 11; /*!< The height of the root note display */
|
||||
@@ -83,39 +88,54 @@ const int LABEL_TEXT_SIZE = 7; /*!< The height of the key label text */
|
||||
* \param _parent the parent instrument plugin window
|
||||
* \todo are the descriptions of the m_startkey and m_lastkey properties correct?
|
||||
*/
|
||||
PianoView::PianoView( QWidget * _parent ) :
|
||||
QWidget( _parent ), /*!< Our parent */
|
||||
ModelView( NULL, this ), /*!< Our view Model */
|
||||
m_piano( NULL ), /*!< Our piano Model */
|
||||
m_startKey( Key_C + Octave_3*KeysPerOctave ), /*!< The first key displayed? */
|
||||
m_lastKey( -1 ) /*!< The last key displayed? */
|
||||
PianoView::PianoView(QWidget *parent) :
|
||||
QWidget(parent), /*!< Our parent */
|
||||
ModelView(nullptr, this), /*!< Our view Model */
|
||||
m_piano(nullptr), /*!< Our piano Model */
|
||||
m_startKey(Key_C + Octave_3*KeysPerOctave), /*!< The first key displayed? */
|
||||
m_lastKey(-1), /*!< The last key displayed? */
|
||||
m_movedNoteModel(nullptr) /*!< Key marker which is being moved */
|
||||
{
|
||||
if( s_whiteKeyPm == NULL )
|
||||
if (s_whiteKeyPm == nullptr)
|
||||
{
|
||||
s_whiteKeyPm = new QPixmap( embed::getIconPixmap( "white_key" ) );
|
||||
s_whiteKeyPm = new QPixmap(embed::getIconPixmap("white_key"));
|
||||
}
|
||||
if( s_blackKeyPm == NULL )
|
||||
if (s_blackKeyPm == nullptr)
|
||||
{
|
||||
s_blackKeyPm = new QPixmap( embed::getIconPixmap( "black_key" ) );
|
||||
s_blackKeyPm = new QPixmap(embed::getIconPixmap("black_key"));
|
||||
}
|
||||
if( s_whiteKeyPressedPm == NULL )
|
||||
if (s_whiteKeyPressedPm == nullptr)
|
||||
{
|
||||
s_whiteKeyPressedPm = new QPixmap( embed::getIconPixmap( "white_key_pressed" ) );
|
||||
s_whiteKeyPressedPm = new QPixmap(embed::getIconPixmap("white_key_pressed"));
|
||||
}
|
||||
if ( s_blackKeyPressedPm == NULL )
|
||||
if (s_blackKeyPressedPm == nullptr)
|
||||
{
|
||||
s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( "black_key_pressed" ) );
|
||||
s_blackKeyPressedPm = new QPixmap(embed::getIconPixmap("black_key_pressed"));
|
||||
}
|
||||
if (s_whiteKeyDisabledPm == nullptr)
|
||||
{
|
||||
s_whiteKeyDisabledPm = new QPixmap(embed::getIconPixmap("white_key_disabled"));
|
||||
}
|
||||
if (s_blackKeyDisabledPm == nullptr)
|
||||
{
|
||||
s_blackKeyDisabledPm = new QPixmap(embed::getIconPixmap("black_key_disabled"));
|
||||
}
|
||||
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
setMaximumWidth( WhiteKeysPerOctave * NumOctaves * PW_WHITE_KEY_WIDTH );
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, true);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
// Black keys are drawn halfway between successive white keys, so they do not
|
||||
// contribute to the total width. Half of a black key is added in case the last
|
||||
// octave is incomplete and ends with a black key. Drawing always starts at
|
||||
// a white key, so no similar modification is needed at the beginning.
|
||||
setMaximumWidth(Piano::NumWhiteKeys * PW_WHITE_KEY_WIDTH +
|
||||
(Piano::isBlackKey(NumKeys-1) ? PW_BLACK_KEY_WIDTH / 2 : 0));
|
||||
|
||||
// create scrollbar at the bottom
|
||||
m_pianoScroll = new QScrollBar( Qt::Horizontal, this );
|
||||
m_pianoScroll->setSingleStep( 1 );
|
||||
m_pianoScroll->setPageStep( 20 );
|
||||
m_pianoScroll->setValue( Octave_3 * WhiteKeysPerOctave );
|
||||
m_pianoScroll->setValue(Octave_3 * Piano::WhiteKeysPerOctave);
|
||||
|
||||
// and connect it to this widget
|
||||
connect( m_pianoScroll, SIGNAL( valueChanged( int ) ),
|
||||
@@ -127,7 +147,6 @@ PianoView::PianoView( QWidget * _parent ) :
|
||||
layout->setMargin( 0 );
|
||||
layout->addSpacing( PIANO_BASE+PW_WHITE_KEY_HEIGHT );
|
||||
layout->addWidget( m_pianoScroll );
|
||||
|
||||
}
|
||||
|
||||
/*! \brief Map a keyboard key being pressed to a note in our keyboard view
|
||||
@@ -281,17 +300,16 @@ int PianoView::getKeyFromKeyEvent( QKeyEvent * _ke )
|
||||
void PianoView::modelChanged()
|
||||
{
|
||||
m_piano = castModel<Piano>();
|
||||
if( m_piano != NULL )
|
||||
if (m_piano != nullptr)
|
||||
{
|
||||
connect( m_piano->instrumentTrack()->baseNoteModel(), SIGNAL( dataChanged() ),
|
||||
this, SLOT( update() ) );
|
||||
connect(m_piano->instrumentTrack()->baseNoteModel(), SIGNAL(dataChanged()), this, SLOT(update()));
|
||||
connect(m_piano->instrumentTrack()->firstKeyModel(), SIGNAL(dataChanged()), this, SLOT(update()));
|
||||
connect(m_piano->instrumentTrack()->lastKeyModel(), SIGNAL(dataChanged()), this, SLOT(update()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// gets the key from the given mouse-position
|
||||
/*! \brief Get the key from the mouse position in the piano display
|
||||
*
|
||||
@@ -368,10 +386,10 @@ int PianoView::getKeyFromMouse( const QPoint & _p ) const
|
||||
*
|
||||
* \param _new_pos the new key position.
|
||||
*/
|
||||
void PianoView::pianoScrolled( int _new_pos )
|
||||
void PianoView::pianoScrolled(int new_pos)
|
||||
{
|
||||
m_startKey = WhiteKeys[_new_pos % WhiteKeysPerOctave]+
|
||||
( _new_pos / WhiteKeysPerOctave ) * KeysPerOctave;
|
||||
m_startKey = WhiteKeys[new_pos % Piano::WhiteKeysPerOctave] +
|
||||
(new_pos / Piano::WhiteKeysPerOctave) * KeysPerOctave;
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -381,21 +399,27 @@ void PianoView::pianoScrolled( int _new_pos )
|
||||
|
||||
/*! \brief Handle a context menu selection on the piano display view
|
||||
*
|
||||
* \param _me the ContextMenuEvent to handle.
|
||||
* \param me the ContextMenuEvent to handle.
|
||||
* \todo Is this right, or does this create the context menu?
|
||||
*/
|
||||
void PianoView::contextMenuEvent( QContextMenuEvent * _me )
|
||||
void PianoView::contextMenuEvent(QContextMenuEvent *me)
|
||||
{
|
||||
if( _me->pos().y() > PIANO_BASE || m_piano == NULL )
|
||||
if (me->pos().y() > PIANO_BASE || m_piano == nullptr ||
|
||||
// m_piano->instrumentTrack()->microtuner()->keyRangeImport())
|
||||
false)
|
||||
{
|
||||
QWidget::contextMenuEvent( _me );
|
||||
QWidget::contextMenuEvent(me);
|
||||
return;
|
||||
}
|
||||
|
||||
CaptionMenu contextMenu( tr( "Base note" ) );
|
||||
AutomatableModelView amv( m_piano->instrumentTrack()->baseNoteModel(), &contextMenu );
|
||||
amv.addDefaultActions( &contextMenu );
|
||||
contextMenu.exec( QCursor::pos() );
|
||||
// check which control element is closest to the mouse and open the appropriate menu
|
||||
QString title;
|
||||
IntModel *noteModel = getNearestMarker(getKeyFromMouse(me->pos()), &title);
|
||||
|
||||
CaptionMenu contextMenu(title);
|
||||
AutomatableModelView amv(noteModel, &contextMenu);
|
||||
amv.addDefaultActions(&contextMenu);
|
||||
contextMenu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
|
||||
@@ -417,54 +441,56 @@ void PianoView::contextMenuEvent( QContextMenuEvent * _me )
|
||||
*
|
||||
* We finally update ourselves to show the key press
|
||||
*
|
||||
* \param _me the mouse click to handle.
|
||||
* \param me the mouse click to handle.
|
||||
*/
|
||||
void PianoView::mousePressEvent( QMouseEvent * _me )
|
||||
void PianoView::mousePressEvent(QMouseEvent *me)
|
||||
{
|
||||
if( _me->button() == Qt::LeftButton && m_piano != NULL )
|
||||
if (me->button() == Qt::LeftButton && m_piano != nullptr)
|
||||
{
|
||||
// get pressed key
|
||||
int key_num = getKeyFromMouse( _me->pos() );
|
||||
if( _me->pos().y() > PIANO_BASE )
|
||||
int key_num = getKeyFromMouse(me->pos());
|
||||
if (me->pos().y() > PIANO_BASE)
|
||||
{
|
||||
int y_diff = _me->pos().y() - PIANO_BASE;
|
||||
int velocity = (int)( ( float ) y_diff /
|
||||
( Piano::isWhiteKey( key_num ) ?
|
||||
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
|
||||
(float) m_piano->instrumentTrack()->midiPort()->baseVelocity() );
|
||||
if( y_diff < 0 )
|
||||
int y_diff = me->pos().y() - PIANO_BASE;
|
||||
int velocity = static_cast<int>(
|
||||
static_cast<float>(y_diff) / getKeyHeight(key_num) *
|
||||
m_piano->instrumentTrack()->midiPort()->baseVelocity());
|
||||
if (y_diff < 0)
|
||||
{
|
||||
velocity = 0;
|
||||
}
|
||||
else if( y_diff >
|
||||
( Piano::isWhiteKey( key_num ) ?
|
||||
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
|
||||
else if (y_diff > getKeyHeight(key_num))
|
||||
{
|
||||
velocity = m_piano->instrumentTrack()->midiPort()->baseVelocity();
|
||||
}
|
||||
// set note on
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOn, -1, key_num, velocity ) );
|
||||
m_piano->setKeyState( key_num, true );
|
||||
m_piano->midiEventProcessor()->processInEvent(MidiEvent(MidiNoteOn, -1, key_num, velocity));
|
||||
m_piano->setKeyState(key_num, true);
|
||||
m_lastKey = key_num;
|
||||
|
||||
emit keyPressed( key_num );
|
||||
emit keyPressed(key_num);
|
||||
}
|
||||
else
|
||||
// else if (!m_piano->instrumentTrack()->microtuner()->keyRangeImport())
|
||||
else if (true)
|
||||
{
|
||||
if( _me->modifiers() & Qt::ControlModifier )
|
||||
// upper section, select which marker (base / first / last note) will be moved
|
||||
m_movedNoteModel = getNearestMarker(key_num);
|
||||
|
||||
if (me->modifiers() & Qt::ControlModifier)
|
||||
{
|
||||
new StringPairDrag( "automatable_model",
|
||||
QString::number( m_piano->instrumentTrack()->baseNoteModel()->id() ),
|
||||
QPixmap(), this );
|
||||
_me->accept();
|
||||
new StringPairDrag("automatable_model", QString::number(m_movedNoteModel->id()), QPixmap(), this);
|
||||
me->accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_piano->instrumentTrack()->baseNoteModel()->setInitValue( (float) key_num );
|
||||
|
||||
emit baseNoteChanged();
|
||||
m_movedNoteModel->setInitValue(static_cast<float>(key_num));
|
||||
if (m_movedNoteModel == m_piano->instrumentTrack()->baseNoteModel()) { emit baseNoteChanged(); } // TODO: not actually used by anything?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_movedNoteModel = nullptr;
|
||||
}
|
||||
|
||||
// and let the user see that he pressed a key... :)
|
||||
update();
|
||||
@@ -486,7 +512,7 @@ void PianoView::mouseReleaseEvent( QMouseEvent * )
|
||||
{
|
||||
if( m_lastKey != -1 )
|
||||
{
|
||||
if( m_piano != NULL )
|
||||
if( m_piano != nullptr )
|
||||
{
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, m_lastKey, 0 ) );
|
||||
m_piano->setKeyState( m_lastKey, false );
|
||||
@@ -496,6 +522,7 @@ void PianoView::mouseReleaseEvent( QMouseEvent * )
|
||||
update();
|
||||
|
||||
m_lastKey = -1;
|
||||
m_movedNoteModel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +545,7 @@ void PianoView::mouseReleaseEvent( QMouseEvent * )
|
||||
*/
|
||||
void PianoView::mouseMoveEvent( QMouseEvent * _me )
|
||||
{
|
||||
if( m_piano == NULL )
|
||||
if( m_piano == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -561,9 +588,10 @@ void PianoView::mouseMoveEvent( QMouseEvent * _me )
|
||||
m_piano->setKeyState( key_num, true );
|
||||
m_lastKey = key_num;
|
||||
}
|
||||
else
|
||||
else if (m_movedNoteModel != nullptr)
|
||||
{
|
||||
m_piano->instrumentTrack()->baseNoteModel()->setInitValue( (float) key_num );
|
||||
// upper section, move the base / first / last note marker
|
||||
m_movedNoteModel->setInitValue(static_cast<float>(key_num));
|
||||
}
|
||||
}
|
||||
// and let the user see that he pressed a key... :)
|
||||
@@ -594,7 +622,7 @@ void PianoView::keyPressEvent( QKeyEvent * _ke )
|
||||
|
||||
if( _ke->isAutoRepeat() == false && key_num > -1 )
|
||||
{
|
||||
if( m_piano != NULL )
|
||||
if( m_piano != nullptr )
|
||||
{
|
||||
m_piano->handleKeyPress( key_num );
|
||||
_ke->accept();
|
||||
@@ -622,7 +650,7 @@ void PianoView::keyReleaseEvent( QKeyEvent * _ke )
|
||||
( DefaultOctave - 1 ) * KeysPerOctave;
|
||||
if( _ke->isAutoRepeat() == false && key_num > -1 )
|
||||
{
|
||||
if( m_piano != NULL )
|
||||
if( m_piano != nullptr )
|
||||
{
|
||||
m_piano->handleKeyRelease( key_num );
|
||||
_ke->accept();
|
||||
@@ -646,7 +674,7 @@ void PianoView::keyReleaseEvent( QKeyEvent * _ke )
|
||||
*/
|
||||
void PianoView::focusOutEvent( QFocusEvent * )
|
||||
{
|
||||
if( m_piano == NULL )
|
||||
if( m_piano == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -654,7 +682,7 @@ void PianoView::focusOutEvent( QFocusEvent * )
|
||||
// focus just switched to another control inside the instrument track
|
||||
// window we live in?
|
||||
if( parentWidget()->parentWidget()->focusWidget() != this &&
|
||||
parentWidget()->parentWidget()->focusWidget() != NULL &&
|
||||
parentWidget()->parentWidget()->focusWidget() != nullptr &&
|
||||
!(parentWidget()->parentWidget()->
|
||||
focusWidget()->inherits( "QLineEdit" ) ||
|
||||
parentWidget()->parentWidget()->
|
||||
@@ -691,14 +719,13 @@ void PianoView::focusInEvent( QFocusEvent * )
|
||||
* After resizing we need to adjust range of scrollbar for not allowing
|
||||
* to scroll too far to the right.
|
||||
*
|
||||
* \param _event resize-event object (unused)
|
||||
* \param event resize-event object (unused)
|
||||
*/
|
||||
void PianoView::resizeEvent( QResizeEvent * _event )
|
||||
void PianoView::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent( _event );
|
||||
m_pianoScroll->setRange( 0, WhiteKeysPerOctave * NumOctaves -
|
||||
(int) ceil( (float) width() /
|
||||
PW_WHITE_KEY_WIDTH ) );
|
||||
QWidget::resizeEvent(event);
|
||||
m_pianoScroll->setRange(0, Piano::NumWhiteKeys -
|
||||
static_cast<int>(floor(static_cast<float>(width()) / PW_WHITE_KEY_WIDTH)));
|
||||
}
|
||||
|
||||
|
||||
@@ -758,6 +785,45 @@ int PianoView::getKeyX( int _key_num ) const
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Return the width of a given key
|
||||
*/
|
||||
int PianoView::getKeyWidth(int key_num) const
|
||||
{
|
||||
return Piano::isWhiteKey(key_num) ? PW_WHITE_KEY_WIDTH : PW_BLACK_KEY_WIDTH;
|
||||
}
|
||||
|
||||
/*! \brief Return the height of a given key
|
||||
*/
|
||||
int PianoView::getKeyHeight(int key_num) const
|
||||
{
|
||||
return Piano::isWhiteKey(key_num) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Return model and title of the marker closest to the given key
|
||||
*/
|
||||
IntModel* PianoView::getNearestMarker(int key, QString* title)
|
||||
{
|
||||
const int base = m_piano->instrumentTrack()->baseNote();
|
||||
const int first = m_piano->instrumentTrack()->firstKey();
|
||||
const int last = m_piano->instrumentTrack()->lastKey();
|
||||
|
||||
if (abs(key - base) < abs(key - first) && abs(key - base) < abs(key - last))
|
||||
{
|
||||
if (title) {*title = tr("Base note");}
|
||||
return m_piano->instrumentTrack()->baseNoteModel();
|
||||
}
|
||||
else if (abs(key - first) < abs(key - last))
|
||||
{
|
||||
if (title) {*title = tr("First note");}
|
||||
return m_piano->instrumentTrack()->firstKeyModel();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (title) {*title = tr("Last note");}
|
||||
return m_piano->instrumentTrack()->lastKeyModel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Paint the piano display view in response to an event
|
||||
@@ -786,89 +852,119 @@ void PianoView::paintEvent( QPaintEvent * )
|
||||
|
||||
p.setPen( Qt::white );
|
||||
|
||||
const int base_key = ( m_piano != NULL ) ?
|
||||
m_piano->instrumentTrack()->baseNoteModel()->value() : 0;
|
||||
|
||||
QColor baseKeyColor = QApplication::palette().color( QPalette::Active,
|
||||
QPalette::BrightText );
|
||||
if( Piano::isWhiteKey( base_key ) )
|
||||
// Controls for first / last / base key models are shown only if microtuner or its key range import are disabled
|
||||
// if (m_piano != nullptr && !m_piano->instrumentTrack()->microtuner()->keyRangeImport())
|
||||
if (m_piano != nullptr && true)
|
||||
{
|
||||
p.fillRect( QRect( getKeyX( base_key ), 1, PW_WHITE_KEY_WIDTH-1,
|
||||
PIANO_BASE-2 ), baseKeyColor );
|
||||
}
|
||||
else
|
||||
{
|
||||
p.fillRect( QRect( getKeyX( base_key ) + 1, 1,
|
||||
PW_BLACK_KEY_WIDTH - 1, PIANO_BASE - 2 ), baseKeyColor);
|
||||
}
|
||||
// Draw the base note marker and first / last note boundary markers
|
||||
const int base_key = m_piano->instrumentTrack()->baseNoteModel()->value();
|
||||
const int first_key = m_piano->instrumentTrack()->firstKeyModel()->value();
|
||||
const int last_key = m_piano->instrumentTrack()->lastKeyModel()->value();
|
||||
QColor marker_color = QApplication::palette().color(QPalette::Active, QPalette::BrightText);
|
||||
|
||||
// - prepare triangle shapes for start / end markers
|
||||
QPainterPath first_marker(QPoint(getKeyX(first_key) + 1, 1));
|
||||
first_marker.lineTo(getKeyX(first_key) + 1, PIANO_BASE);
|
||||
first_marker.lineTo(getKeyX(first_key) + PIANO_BASE / 2 + 1, PIANO_BASE / 2);
|
||||
|
||||
QPainterPath last_marker(QPoint(getKeyX(last_key) + getKeyWidth(last_key), 1));
|
||||
last_marker.lineTo(getKeyX(last_key) + getKeyWidth(last_key), PIANO_BASE);
|
||||
last_marker.lineTo(getKeyX(last_key) + getKeyWidth(last_key) - PIANO_BASE / 2, PIANO_BASE / 2);
|
||||
|
||||
// - fill all markers
|
||||
p.fillRect(QRect(getKeyX(base_key), 1, getKeyWidth(base_key) - 1, PIANO_BASE - 2), marker_color);
|
||||
p.fillPath(first_marker, marker_color);
|
||||
p.fillPath(last_marker, marker_color);
|
||||
}
|
||||
|
||||
int cur_key = m_startKey;
|
||||
|
||||
// draw all white keys...
|
||||
for( int x = 0; x < width(); )
|
||||
for (int x = 0; x < width();)
|
||||
{
|
||||
while( Piano::isBlackKey( cur_key ) )
|
||||
while (Piano::isBlackKey(cur_key))
|
||||
{
|
||||
++cur_key;
|
||||
}
|
||||
|
||||
// draw pressed or not pressed key, depending on state of
|
||||
// current key
|
||||
if( m_piano && m_piano->isKeyPressed( cur_key ) )
|
||||
// draw normal, pressed or disabled key, depending on state and position of current key
|
||||
if (m_piano &&
|
||||
cur_key >= m_piano->instrumentTrack()->firstKeyModel()->value() &&
|
||||
cur_key <= m_piano->instrumentTrack()->lastKeyModel()->value())
|
||||
{
|
||||
p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPressedPm );
|
||||
if (m_piano && m_piano->isKeyPressed(cur_key))
|
||||
{
|
||||
p.drawPixmap(x, PIANO_BASE, *s_whiteKeyPressedPm);
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap(x, PIANO_BASE, *s_whiteKeyPm);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPm );
|
||||
p.drawPixmap(x, PIANO_BASE, *s_whiteKeyDisabledPm);
|
||||
}
|
||||
|
||||
x += PW_WHITE_KEY_WIDTH;
|
||||
|
||||
if( (Keys) (cur_key%KeysPerOctave) == Key_C )
|
||||
if ((Keys)(cur_key % KeysPerOctave) == Key_C)
|
||||
{
|
||||
// label key of note C with "C" and number of current
|
||||
// octave
|
||||
p.drawText( x - PW_WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2,
|
||||
QString( "C" ) + QString::number(
|
||||
cur_key / KeysPerOctave, 10 ) );
|
||||
// label key of note C with "C" and number of current octave
|
||||
p.drawText(x - PW_WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2,
|
||||
QString("C") + QString::number(FirstOctave + cur_key / KeysPerOctave));
|
||||
}
|
||||
++cur_key;
|
||||
}
|
||||
|
||||
|
||||
// reset all values, because now we're going to draw all black keys
|
||||
cur_key = m_startKey;
|
||||
int white_cnt = 0;
|
||||
|
||||
int startKey = m_startKey;
|
||||
if( startKey > 0 && Piano::isBlackKey( (Keys)(--startKey) ) )
|
||||
if (startKey > 0 && Piano::isBlackKey(static_cast<Keys>(--startKey)))
|
||||
{
|
||||
if( m_piano && m_piano->isKeyPressed( startKey ) )
|
||||
if (m_piano &&
|
||||
startKey >= m_piano->instrumentTrack()->firstKeyModel()->value() &&
|
||||
startKey <= m_piano->instrumentTrack()->lastKeyModel()->value())
|
||||
{
|
||||
p.drawPixmap( 0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm );
|
||||
if (m_piano && m_piano->isKeyPressed(startKey))
|
||||
{
|
||||
p.drawPixmap(0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm);
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap(0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap( 0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm );
|
||||
p.drawPixmap(0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyDisabledPm);
|
||||
}
|
||||
}
|
||||
|
||||
// now draw all black keys...
|
||||
for( int x = 0; x < width(); )
|
||||
for (int x = 0; x < width();)
|
||||
{
|
||||
if( Piano::isBlackKey( cur_key ) )
|
||||
if (Piano::isBlackKey(cur_key))
|
||||
{
|
||||
// draw pressed or not pressed key, depending on
|
||||
// state of current key
|
||||
if( m_piano && m_piano->isKeyPressed( cur_key ) )
|
||||
// draw normal, pressed or disabled key, depending on state and position of current key
|
||||
if (m_piano &&
|
||||
cur_key >= m_piano->instrumentTrack()->firstKeyModel()->value() &&
|
||||
cur_key <= m_piano->instrumentTrack()->lastKeyModel()->value())
|
||||
{
|
||||
p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm );
|
||||
if (m_piano && m_piano->isKeyPressed(cur_key))
|
||||
{
|
||||
p.drawPixmap(x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm);
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap(x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm );
|
||||
p.drawPixmap(x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyDisabledPm);
|
||||
}
|
||||
x += PW_WHITE_KEY_WIDTH;
|
||||
white_cnt = 0;
|
||||
@@ -878,17 +974,16 @@ void PianoView::paintEvent( QPaintEvent * )
|
||||
// simple workaround for increasing x if there were two
|
||||
// white keys (e.g. between E and F)
|
||||
++white_cnt;
|
||||
if( white_cnt > 1 )
|
||||
if (white_cnt > 1)
|
||||
{
|
||||
x += PW_WHITE_KEY_WIDTH;
|
||||
}
|
||||
}
|
||||
++cur_key;
|
||||
// stop drawing when all keys are drawn, even if an extra black key could fit
|
||||
if (++cur_key == NumKeys) {break;}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QMargins>
|
||||
#include <QMdiArea>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
#include <QPointer>
|
||||
#include <QScrollBar>
|
||||
@@ -121,11 +122,11 @@ QPixmap* PianoRoll::s_toolKnife = nullptr;
|
||||
|
||||
TextFloat * PianoRoll::s_textFloat = NULL;
|
||||
|
||||
static QString s_noteStrings[12] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||
static QString s_noteStrings[12] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||
|
||||
static QString getNoteString( int key )
|
||||
static QString getNoteString(int key)
|
||||
{
|
||||
return s_noteStrings[key % 12] + QString::number( static_cast<int>( key / KeysPerOctave ) );
|
||||
return s_noteStrings[key % 12] + QString::number(static_cast<int>(FirstOctave + key / KeysPerOctave));
|
||||
}
|
||||
|
||||
// used for drawing of piano
|
||||
@@ -174,7 +175,6 @@ PianoRoll::PianoRoll() :
|
||||
m_userSetNotesEditHeight(100),
|
||||
m_ppb( DEFAULT_PR_PPB ),
|
||||
m_keyLineHeight(DEFAULT_KEY_LINE_HEIGHT),
|
||||
m_octaveHeight(m_keyLineHeight * KeysPerOctave),
|
||||
m_whiteKeySmallHeight(qFloor(m_keyLineHeight * 1.5)),
|
||||
m_whiteKeyBigHeight(m_keyLineHeight * 2),
|
||||
m_blackKeyHeight(m_keyLineHeight),
|
||||
@@ -886,11 +886,10 @@ void PianoRoll::setCurrentPattern( Pattern* newPattern )
|
||||
}
|
||||
}
|
||||
|
||||
if( total_notes > 0 )
|
||||
if (total_notes > 0)
|
||||
{
|
||||
central_key = central_key / total_notes -
|
||||
( KeysPerOctave * NumOctaves - m_totalKeysToScroll ) / 2;
|
||||
m_startKey = qBound( 0, central_key, NumOctaves * KeysPerOctave );
|
||||
central_key = central_key / total_notes - (NumKeys - m_totalKeysToScroll) / 2;
|
||||
m_startKey = qBound(0, central_key, NumKeys);
|
||||
}
|
||||
|
||||
// resizeEvent() does the rest for us (scrolling, range-checking
|
||||
@@ -904,6 +903,9 @@ void PianoRoll::setCurrentPattern( Pattern* newPattern )
|
||||
connect( m_pattern->instrumentTrack(), SIGNAL( midiNoteOff( const Note& ) ), this, SLOT( finishRecordNote( const Note& ) ) );
|
||||
connect( m_pattern->instrumentTrack()->pianoModel(), SIGNAL( dataChanged() ), this, SLOT( update() ) );
|
||||
|
||||
connect(m_pattern->instrumentTrack()->firstKeyModel(), SIGNAL(dataChanged()), this, SLOT(update()));
|
||||
connect(m_pattern->instrumentTrack()->lastKeyModel(), SIGNAL(dataChanged()), this, SLOT(update()));
|
||||
|
||||
update();
|
||||
emit currentPatternChanged();
|
||||
}
|
||||
@@ -3007,8 +3009,8 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
|
||||
f.setPixelSize(m_keyLineHeight * 0.8);
|
||||
p.setFont(f); // font size doesn't change without this for some reason
|
||||
QFontMetrics fontMetrics(p.font());
|
||||
// G4 is one of the widest
|
||||
QRect const boundingRect = fontMetrics.boundingRect(QString("G4"));
|
||||
// G-1 is one of the widest; plus one pixel margin for the shadow
|
||||
QRect const boundingRect = fontMetrics.boundingRect(QString("G-1")) + QMargins(0, 0, 1, 0);
|
||||
|
||||
// Order of drawing
|
||||
// - vertical quantization lines
|
||||
@@ -3160,6 +3162,8 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
|
||||
const int key,
|
||||
const int yb)
|
||||
{
|
||||
const bool mapped = m_pattern->instrumentTrack()->firstKeyModel()->value() <= key &&
|
||||
m_pattern->instrumentTrack()->lastKeyModel()->value() >= key;
|
||||
const bool pressed = m_pattern->instrumentTrack()->pianoModel()->isKeyPressed(key);
|
||||
const int keyCode = key % KeysPerOctave;
|
||||
const int yt = yb - gridCorrection(key);
|
||||
@@ -3171,10 +3175,26 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
|
||||
{
|
||||
case PR_WHITE_KEY_SMALL:
|
||||
case PR_WHITE_KEY_BIG:
|
||||
p.setBrush(pressed ? m_whiteKeyActiveBackground : m_whiteKeyInactiveBackground);
|
||||
if (mapped)
|
||||
{
|
||||
if (pressed) { p.setBrush(m_whiteKeyActiveBackground); }
|
||||
else { p.setBrush(m_whiteKeyInactiveBackground); }
|
||||
}
|
||||
else
|
||||
{
|
||||
p.setBrush(m_whiteKeyDisabledBackground);
|
||||
}
|
||||
break;
|
||||
case PR_BLACK_KEY:
|
||||
p.setBrush(pressed ? m_blackKeyActiveBackground : m_blackKeyInactiveBackground);
|
||||
if (mapped)
|
||||
{
|
||||
if (pressed) { p.setBrush(m_blackKeyActiveBackground); }
|
||||
else { p.setBrush(m_blackKeyInactiveBackground); }
|
||||
}
|
||||
else
|
||||
{
|
||||
p.setBrush(m_blackKeyDisabledBackground);
|
||||
}
|
||||
}
|
||||
// draw key
|
||||
p.drawRect(PIANO_X, yt, kw, kh);
|
||||
@@ -3693,7 +3713,7 @@ void PianoRoll::updateScrollbars()
|
||||
}
|
||||
|
||||
// responsible for moving/resizing scrollbars after window-resizing
|
||||
void PianoRoll::resizeEvent(QResizeEvent * re)
|
||||
void PianoRoll::resizeEvent(QResizeEvent* re)
|
||||
{
|
||||
updatePositionLineHeight();
|
||||
updateScrollbars();
|
||||
@@ -4261,16 +4281,14 @@ void PianoRoll::updateYScroll()
|
||||
height() - PR_TOP_MARGIN -
|
||||
SCROLLBAR_SIZE);
|
||||
|
||||
int total_pixels = m_octaveHeight * NumOctaves - (height() -
|
||||
PR_TOP_MARGIN - PR_BOTTOM_MARGIN -
|
||||
m_notesEditHeight);
|
||||
m_totalKeysToScroll = qMax(0, total_pixels * KeysPerOctave / m_octaveHeight);
|
||||
const int visible_space = keyAreaBottom() - keyAreaTop();
|
||||
m_totalKeysToScroll = qMax(0, NumKeys - 1 - visible_space / m_keyLineHeight);
|
||||
|
||||
m_topBottomScroll->setRange(0, m_totalKeysToScroll);
|
||||
|
||||
if(m_startKey > m_totalKeysToScroll)
|
||||
{
|
||||
m_startKey = qMax(0, m_totalKeysToScroll);
|
||||
m_startKey = m_totalKeysToScroll;
|
||||
}
|
||||
m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey);
|
||||
}
|
||||
@@ -4517,7 +4535,6 @@ void PianoRoll::zoomingChanged()
|
||||
void PianoRoll::zoomingYChanged()
|
||||
{
|
||||
m_keyLineHeight = m_zoomYLevels[m_zoomingYModel.value()] * DEFAULT_KEY_LINE_HEIGHT;
|
||||
m_octaveHeight = m_keyLineHeight * KeysPerOctave;
|
||||
m_whiteKeySmallHeight = qFloor(m_keyLineHeight * 1.5);
|
||||
m_whiteKeyBigHeight = m_keyLineHeight * 2;
|
||||
m_blackKeyHeight = m_keyLineHeight; //round(m_keyLineHeight * 1.3333);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "AutomationPattern.h"
|
||||
#include "BBTrack.h"
|
||||
#include "CaptionMenu.h"
|
||||
#include "ComboBox.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "ControllerConnection.h"
|
||||
#include "DataFile.h"
|
||||
@@ -61,6 +62,7 @@
|
||||
#include "LcdSpinBox.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "LeftRightNav.h"
|
||||
#include "lmms_constants.h"
|
||||
#include "MainWindow.h"
|
||||
#include "MidiClient.h"
|
||||
#include "MidiPortMenu.h"
|
||||
@@ -92,9 +94,10 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
m_sustainPedalPressed( false ),
|
||||
m_silentBuffersProcessed( false ),
|
||||
m_previewMode( false ),
|
||||
m_baseNoteModel(0, 0, NumKeys - 1, this, tr("Base note")),
|
||||
m_firstKeyModel(0, 0, NumKeys - 1, this, tr("First note")),
|
||||
m_lastKeyModel(0, 0, NumKeys - 1, this, tr("Last note")),
|
||||
m_hasAutoMidiDev( false ),
|
||||
m_baseNoteModel( 0, 0, KeysPerOctave * NumOctaves - 1, this,
|
||||
tr( "Base note" ) ),
|
||||
m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr( "Volume" ) ),
|
||||
m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ),
|
||||
m_audioPort( tr( "unnamed_track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ),
|
||||
@@ -106,11 +109,14 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
m_soundShaping( this ),
|
||||
m_arpeggio( this ),
|
||||
m_noteStacking( this ),
|
||||
m_piano( this )
|
||||
m_piano(this)
|
||||
// m_microtuner(this)
|
||||
{
|
||||
m_pitchModel.setCenterValue( 0 );
|
||||
m_panningModel.setCenterValue( DefaultPanning );
|
||||
m_baseNoteModel.setInitValue( DefaultKey );
|
||||
m_firstKeyModel.setInitValue(0);
|
||||
m_lastKeyModel.setInitValue(NumKeys - 1);
|
||||
|
||||
m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels()-1, 1);
|
||||
|
||||
@@ -138,14 +144,10 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
|
||||
setName( tr( "Default preset" ) );
|
||||
|
||||
connect( &m_baseNoteModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( updateBaseNote() ), Qt::DirectConnection );
|
||||
connect( &m_pitchModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( updatePitch() ), Qt::DirectConnection );
|
||||
connect( &m_pitchRangeModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( updatePitchRange() ), Qt::DirectConnection );
|
||||
connect( &m_effectChannelModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( updateEffectChannel() ), Qt::DirectConnection );
|
||||
connect(&m_baseNoteModel, SIGNAL(dataChanged()), this, SLOT(updateBaseNote()), Qt::DirectConnection);
|
||||
connect(&m_pitchModel, SIGNAL(dataChanged()), this, SLOT(updatePitch()), Qt::DirectConnection);
|
||||
connect(&m_pitchRangeModel, SIGNAL(dataChanged()), this, SLOT(updatePitchRange()), Qt::DirectConnection);
|
||||
connect(&m_effectChannelModel, SIGNAL(dataChanged()), this, SLOT(updateEffectChannel()), Qt::DirectConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +158,15 @@ int InstrumentTrack::baseNote() const
|
||||
return m_baseNoteModel.value() - mp;
|
||||
}
|
||||
|
||||
int InstrumentTrack::firstKey() const
|
||||
{
|
||||
return m_firstKeyModel.value();
|
||||
}
|
||||
|
||||
int InstrumentTrack::lastKey() const
|
||||
{
|
||||
return m_lastKeyModel.value();
|
||||
}
|
||||
|
||||
|
||||
InstrumentTrack::~InstrumentTrack()
|
||||
@@ -294,7 +305,8 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim
|
||||
case MidiNoteOn:
|
||||
if( event.velocity() > 0 )
|
||||
{
|
||||
if( m_notes[event.key()] == NULL )
|
||||
// play a note only if it is not already playing and if it is within configured bounds
|
||||
if (m_notes[event.key()] == nullptr && event.key() >= firstKey() && event.key() <= lastKey())
|
||||
{
|
||||
NotePlayHandle* nph =
|
||||
NotePlayHandleManager::acquire(
|
||||
@@ -775,6 +787,8 @@ void InstrumentTrack::saveTrackSpecificSettings( QDomDocument& doc, QDomElement
|
||||
|
||||
m_effectChannelModel.saveSettings( doc, thisElement, "fxch" );
|
||||
m_baseNoteModel.saveSettings( doc, thisElement, "basenote" );
|
||||
m_firstKeyModel.saveSettings(doc, thisElement, "firstkey");
|
||||
m_lastKeyModel.saveSettings(doc, thisElement, "lastkey");
|
||||
m_useMasterPitchModel.saveSettings( doc, thisElement, "usemasterpitch");
|
||||
|
||||
// Save MIDI CC stuff
|
||||
@@ -839,6 +853,8 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement
|
||||
m_effectChannelModel.loadSettings( thisElement, "fxch" );
|
||||
}
|
||||
m_baseNoteModel.loadSettings( thisElement, "basenote" );
|
||||
m_firstKeyModel.loadSettings(thisElement, "firstkey");
|
||||
m_lastKeyModel.loadSettings(thisElement, "lastkey");
|
||||
m_useMasterPitchModel.loadSettings( thisElement, "usemasterpitch");
|
||||
|
||||
// clear effect-chain just in case we load an old preset without FX-data
|
||||
@@ -1006,7 +1022,6 @@ void InstrumentTrack::autoAssignMidiDevice(bool assign)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// #### ITV:
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user