diff --git a/data/themes/default/chord.png b/data/themes/default/chord.png new file mode 100644 index 000000000..3f475b0ea Binary files /dev/null and b/data/themes/default/chord.png differ diff --git a/data/themes/default/scale.png b/data/themes/default/scale.png new file mode 100644 index 000000000..3f475b0ea Binary files /dev/null and b/data/themes/default/scale.png differ diff --git a/include/InstrumentFunctions.h b/include/InstrumentFunctions.h index 43a73bbe4..8520acab5 100644 --- a/include/InstrumentFunctions.h +++ b/include/InstrumentFunctions.h @@ -36,12 +36,17 @@ class InstrumentTrack; class notePlayHandle; -const int MAX_CHORD_POLYPHONY = 10; - class ChordCreator : public Model, public JournallingObject { Q_OBJECT + +public: + static const int MAX_CHORD_POLYPHONY = 10; + +private: + typedef Sint8 ChordSemiTones [MAX_CHORD_POLYPHONY]; + public: ChordCreator( Model * _parent ); virtual ~ChordCreator(); @@ -58,22 +63,84 @@ public: } - static struct Chord + struct Chord { - const QString name; - Sint8 interval[MAX_CHORD_POLYPHONY]; - } s_chordTable[]; + private: + QString m_name; + ChordSemiTones m_semiTones; + int m_size; + public: + Chord() : m_size( 0 ) {} - static inline int getChordSize( Chord & _c ) - { - int idx = 0; - while( _c.interval[idx] != -1 ) + Chord( const char * n, const ChordSemiTones & semi_tones ); + + int size() const { - ++idx; + return m_size; } - return idx; - } + + bool isScale() const + { + return size() > 6; + } + + bool isEmpty() const + { + return size() == 0; + } + + bool hasSemiTone( Sint8 semi_tone ) const; + + Sint8 last() const + { + return m_semiTones[size() - 1]; + } + + const QString & getName() const + { + return m_name; + } + + Sint8 operator [] ( int n ) const + { + return m_semiTones[n]; + } + }; + + + struct ChordTable : public QVector + { + private: + ChordTable(); + + struct Init + { + const char * m_name; + ChordSemiTones m_semiTones; + }; + + static Init s_initTable[]; + + public: + static const ChordTable & getInstance() + { + static ChordTable inst; + return inst; + } + + const Chord & getByName( const QString & name, bool is_scale = false ) const; + + const Chord & getScaleByName( const QString & name ) const + { + return getByName( name, true ); + } + + const Chord & getChordByName( const QString & name ) const + { + return getByName( name, false ); + } + }; private: diff --git a/include/piano_roll.h b/include/piano_roll.h index 7780875c1..3bb1c5d68 100644 --- a/include/piano_roll.h +++ b/include/piano_roll.h @@ -139,9 +139,16 @@ protected slots: void zoomingChanged(); void quantizeChanged(); - + + void updateSemiToneMarkerMenu(); + void changeNoteEditMode( int i ); - void markTone( int i ); + void markSemiTone( int i ); + + +signals: + void semiToneMarkerMenuScaleSetEnabled(bool); + void semiToneMarkerMenuChordSetEnabled(bool); private: @@ -171,12 +178,12 @@ private: NoteEditCount // make sure this one is always last }; - enum toneMarkerAction + enum semiToneMarkerAction { - tmmeUnmarkAll, - tmmeMarkCurrent, - tmmeMarkMinorScale, - tmmeMarkMajorScale, + stmaUnmarkAll, + stmaMarkCurrentSemiTone, + stmaMarkCurrentScale, + stmaMarkCurrentChord, }; enum pianoRollKeyTypes @@ -189,8 +196,8 @@ private: QVector m_nemStr; // gui names of each edit mode QMenu * m_noteEditMenu; // when you right click below the key area - QList m_markedTones; - QMenu * m_toneMarkerMenu; // when you right click on the key area + QList m_markedSemiTones; + QMenu * m_semiToneMarkerMenu; // when you right click on the key area pianoRoll(); pianoRoll( const pianoRoll & ); @@ -251,10 +258,14 @@ private: comboBox * m_zoomingComboBox; comboBox * m_quantizeComboBox; comboBox * m_noteLenComboBox; + comboBox * m_scaleComboBox; + comboBox * m_chordComboBox; ComboBoxModel m_zoomingModel; ComboBoxModel m_quantizeModel; ComboBoxModel m_noteLenModel; + ComboBoxModel m_scaleModel; + ComboBoxModel m_chordModel; diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index d806f2228..a20966365 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -33,118 +33,177 @@ -ChordCreator::Chord ChordCreator::s_chordTable[] = +ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { - { ChordCreator::tr( "octave" ), { 0, -1 } }, - { ChordCreator::tr( "Major" ), { 0, 4, 7, -1 } }, - { ChordCreator::tr( "Majb5" ), { 0, 4, 6, -1 } }, - { ChordCreator::tr( "minor" ), { 0, 3, 7, -1 } }, - { ChordCreator::tr( "minb5" ), { 0, 3, 6, -1 } }, - { ChordCreator::tr( "sus2" ), { 0, 2, 7, -1 } }, - { ChordCreator::tr( "sus4" ), { 0, 5, 7, -1 } }, - { ChordCreator::tr( "aug" ), { 0, 4, 8, -1 } }, - { ChordCreator::tr( "augsus4" ), { 0, 5, 8, -1 } }, - { ChordCreator::tr( "tri" ), { 0, 3, 6, 9, -1 } }, + { "octave", { 0, -1 } }, + { "Major" , { 0, 4, 7, -1 } }, + { "Majb5" , { 0, 4, 6, -1 } }, + { "minor" , { 0, 3, 7, -1 } }, + { "minb5" , { 0, 3, 6, -1 } }, + { "sus2" , { 0, 2, 7, -1 } }, + { "sus4" , { 0, 5, 7, -1 } }, + { "aug" , { 0, 4, 8, -1 } }, + { "augsus4" , { 0, 5, 8, -1 } }, + { "tri" , { 0, 3, 6, 9, -1 } }, - { ChordCreator::tr( "6" ), { 0, 4, 7, 9, -1 } }, - { ChordCreator::tr( "6sus4" ), { 0, 5, 7, 9, -1 } }, - { ChordCreator::tr( "6add9" ), { 0, 4, 7, 12, -1 } }, - { ChordCreator::tr( "m6" ), { 0, 3, 7, 9, -1 } }, - { ChordCreator::tr( "m6add9" ), { 0, 3, 7, 9, 14, -1 } }, + { "6" , { 0, 4, 7, 9, -1 } }, + { "6sus4" , { 0, 5, 7, 9, -1 } }, + { "6add9" , { 0, 4, 7, 12, -1 } }, + { "m6" , { 0, 3, 7, 9, -1 } }, + { "m6add9" , { 0, 3, 7, 9, 14, -1 } }, - { ChordCreator::tr( "7" ), { 0, 4, 7, 10, -1 } }, - { ChordCreator::tr( "7sus4" ), { 0, 5, 7, 10, -1 } }, - { ChordCreator::tr( "7#5" ), { 0, 4, 8, 10, -1 } }, - { ChordCreator::tr( "7b5" ), { 0, 4, 6, 10, -1 } }, - { ChordCreator::tr( "7#9" ), { 0, 4, 7, 10, 13, 18, -1 } }, - { ChordCreator::tr( "7b9" ), { 0, 4, 7, 10, 13, 16, -1 } }, - { ChordCreator::tr( "7#5#9" ), { 0, 4, 8, 12, 14, 19, -1 } }, - { ChordCreator::tr( "7#5b9" ), { 0, 4, 8, 12, 14, 17, -1 } }, - { ChordCreator::tr( "7b5b9" ), { 0, 4, 6, 10, 12, 15, -1 } }, - { ChordCreator::tr( "7add11" ), { 0, 4, 7, 10, 17, -1 } }, - { ChordCreator::tr( "7add13" ), { 0, 4, 7, 10, 21, -1 } }, - { ChordCreator::tr( "7#11" ), { 0, 4, 7, 10, 18, -1 } }, - { ChordCreator::tr( "Maj7" ), { 0, 4, 7, 11, -1 } }, - { ChordCreator::tr( "Maj7b5" ), { 0, 4, 6, 11, -1 } }, - { ChordCreator::tr( "Maj7#5" ), { 0, 4, 8, 11, -1 } }, - { ChordCreator::tr( "Maj7#11" ), { 0, 4, 7, 11, 18, -1 } }, - { ChordCreator::tr( "Maj7add13" ), { 0, 4, 7, 11, 21, -1 } }, - { ChordCreator::tr( "m7" ), { 0, 3, 7, 10, -1 } }, - { ChordCreator::tr( "m7b5" ), { 0, 3, 6, 10, -1 } }, - { ChordCreator::tr( "m7b9" ), { 0, 3, 7, 10, 13, -1 } }, - { ChordCreator::tr( "m7add11" ), { 0, 3, 7, 10, 17, -1 } }, - { ChordCreator::tr( "m7add13" ), { 0, 3, 7, 10, 21, -1 } }, - { ChordCreator::tr( "m-Maj7" ), { 0, 3, 7, 11, -1 } }, - { ChordCreator::tr( "m-Maj7add11" ), { 0, 3, 7, 11, 17, -1 } }, - { ChordCreator::tr( "m-Maj7add13" ), { 0, 3, 7, 11, 21, -1 } }, + { "7" , { 0, 4, 7, 10, -1 } }, + { "7sus4" , { 0, 5, 7, 10, -1 } }, + { "7#5" , { 0, 4, 8, 10, -1 } }, + { "7b5" , { 0, 4, 6, 10, -1 } }, + { "7#9" , { 0, 4, 7, 10, 13, 18, -1 } }, + { "7b9" , { 0, 4, 7, 10, 13, 16, -1 } }, + { "7#5#9" , { 0, 4, 8, 12, 14, 19, -1 } }, + { "7#5b9" , { 0, 4, 8, 12, 14, 17, -1 } }, + { "7b5b9" , { 0, 4, 6, 10, 12, 15, -1 } }, + { "7add11" , { 0, 4, 7, 10, 17, -1 } }, + { "7add13" , { 0, 4, 7, 10, 21, -1 } }, + { "7#11" , { 0, 4, 7, 10, 18, -1 } }, + { "Maj7" , { 0, 4, 7, 11, -1 } }, + { "Maj7b5" , { 0, 4, 6, 11, -1 } }, + { "Maj7#5" , { 0, 4, 8, 11, -1 } }, + { "Maj7#11" , { 0, 4, 7, 11, 18, -1 } }, + { "Maj7add13" , { 0, 4, 7, 11, 21, -1 } }, + { "m7" , { 0, 3, 7, 10, -1 } }, + { "m7b5" , { 0, 3, 6, 10, -1 } }, + { "m7b9" , { 0, 3, 7, 10, 13, -1 } }, + { "m7add11" , { 0, 3, 7, 10, 17, -1 } }, + { "m7add13" , { 0, 3, 7, 10, 21, -1 } }, + { "m-Maj7" , { 0, 3, 7, 11, -1 } }, + { "m-Maj7add11" , { 0, 3, 7, 11, 17, -1 } }, + { "m-Maj7add13" , { 0, 3, 7, 11, 21, -1 } }, - { ChordCreator::tr( "9" ), { 0, 4, 7, 10, 14, -1 } }, - { ChordCreator::tr( "9sus4" ), { 0, 5, 7, 10, 14, -1 } }, - { ChordCreator::tr( "add9" ), { 0, 4, 7, 14, -1 } }, - { ChordCreator::tr( "9#5" ), { 0, 4, 8, 10, 14, -1 } }, - { ChordCreator::tr( "9b5" ), { 0, 4, 6, 10, 14, -1 } }, - { ChordCreator::tr( "9#11" ), { 0, 4, 7, 10, 14, 18, -1 } }, - { ChordCreator::tr( "9b13" ), { 0, 4, 7, 10, 14, 20, -1 } }, - { ChordCreator::tr( "Maj9" ), { 0, 4, 7, 11, 14, -1 } }, - { ChordCreator::tr( "Maj9sus4" ), { 0, 5, 7, 11, 15, -1 } }, - { ChordCreator::tr( "Maj9#5" ), { 0, 4, 8, 11, 14, -1 } }, - { ChordCreator::tr( "Maj9#11" ), { 0, 4, 7, 11, 14, 18, -1 } }, - { ChordCreator::tr( "m9" ), { 0, 3, 7, 10, 14, -1 } }, - { ChordCreator::tr( "madd9" ), { 0, 3, 7, 14, -1 } }, - { ChordCreator::tr( "m9b5" ), { 0, 3, 6, 10, 14, -1 } }, - { ChordCreator::tr( "m9-Maj7" ), { 0, 3, 7, 11, 14, -1 } }, + { "9" , { 0, 4, 7, 10, 14, -1 } }, + { "9sus4" , { 0, 5, 7, 10, 14, -1 } }, + { "add9" , { 0, 4, 7, 14, -1 } }, + { "9#5" , { 0, 4, 8, 10, 14, -1 } }, + { "9b5" , { 0, 4, 6, 10, 14, -1 } }, + { "9#11" , { 0, 4, 7, 10, 14, 18, -1 } }, + { "9b13" , { 0, 4, 7, 10, 14, 20, -1 } }, + { "Maj9" , { 0, 4, 7, 11, 14, -1 } }, + { "Maj9sus4" , { 0, 5, 7, 11, 15, -1 } }, + { "Maj9#5" , { 0, 4, 8, 11, 14, -1 } }, + { "Maj9#11" , { 0, 4, 7, 11, 14, 18, -1 } }, + { "m9" , { 0, 3, 7, 10, 14, -1 } }, + { "madd9" , { 0, 3, 7, 14, -1 } }, + { "m9b5" , { 0, 3, 6, 10, 14, -1 } }, + { "m9-Maj7" , { 0, 3, 7, 11, 14, -1 } }, - { ChordCreator::tr( "11" ), { 0, 4, 7, 10, 14, 17, -1 } }, - { ChordCreator::tr( "11b9" ), { 0, 4, 7, 10, 13, 17, -1 } }, - { ChordCreator::tr( "Maj11" ), { 0, 4, 7, 11, 14, 17, -1 } }, - { ChordCreator::tr( "m11" ), { 0, 3, 7, 10, 14, 17, -1 } }, - { ChordCreator::tr( "m-Maj11" ), { 0, 3, 7, 11, 14, 17, -1 } }, + { "11" , { 0, 4, 7, 10, 14, 17, -1 } }, + { "11b9" , { 0, 4, 7, 10, 13, 17, -1 } }, + { "Maj11" , { 0, 4, 7, 11, 14, 17, -1 } }, + { "m11" , { 0, 3, 7, 10, 14, 17, -1 } }, + { "m-Maj11" , { 0, 3, 7, 11, 14, 17, -1 } }, - { ChordCreator::tr( "13" ), { 0, 4, 7, 10, 14, 21, -1 } }, - { ChordCreator::tr( "13#9" ), { 0, 4, 7, 10, 15, 21, -1 } }, - { ChordCreator::tr( "13b9" ), { 0, 4, 7, 10, 13, 21, -1 } }, - { ChordCreator::tr( "13b5b9" ), { 0, 4, 6, 10, 13, 21, -1 } }, - { ChordCreator::tr( "Maj13" ), { 0, 4, 7, 11, 14, 21, -1 } }, - { ChordCreator::tr( "m13" ), { 0, 3, 7, 10, 14, 21, -1 } }, - { ChordCreator::tr( "m-Maj13" ), { 0, 3, 7, 11, 14, 21, -1 } }, - - { ChordCreator::tr( "Major" ), { 0, 2, 4, 5, 7, 9, 11, -1 } }, - { ChordCreator::tr( "Harmonic minor" ), { 0, 2, 3, 5, 7, 8, 11, -1 } }, - { ChordCreator::tr( "Melodic minor" ), { 0, 2, 3, 5, 7, 9, 11, -1 } }, - { ChordCreator::tr( "Whole tone" ), { 0, 2, 4, 6, 8, 10, -1 } }, - { ChordCreator::tr( "Diminished" ), { 0, 2, 3, 5, 6, 8, 9, 11, -1 } }, - { ChordCreator::tr( "Major pentatonic" ), { 0, 2, 4, 7, 10, -1 } }, - { ChordCreator::tr( "Minor pentatonic" ), { 0, 3, 5, 7, 10, -1 } }, - { ChordCreator::tr( "Jap in sen" ), { 0, 1, 5, 7, 10, -1 } }, - { ChordCreator::tr( "Major bebop" ), { 0, 2, 4, 5, 7, 8, 9, 11, -1 } }, - { ChordCreator::tr( "Dominant bebop" ), { 0, 2, 4, 5, 7, 9, 10, 11, -1 } }, - { ChordCreator::tr( "Blues" ), { 0, 3, 5, 6, 7, 10, -1 } }, - { ChordCreator::tr( "Arabic" ), { 0, 1, 4, 5, 7, 8, 11, -1 } }, - { ChordCreator::tr( "Enigmatic" ), { 0, 1, 4, 6, 8, 10, 11, -1 } }, - { ChordCreator::tr( "Neopolitan" ), { 0, 1, 3, 5, 7, 9, 11, -1 } }, - { ChordCreator::tr( "Neopolitan minor" ), { 0, 1, 3, 5, 7, 9, 11, -1 } }, - { ChordCreator::tr( "Hungarian minor" ), { 0, 2, 3, 6, 7, 9, 11, -1 } }, - { ChordCreator::tr( "Dorian" ), { 0, 2, 3, 5, 7, 9, 10, -1 } }, - { ChordCreator::tr( "Phrygolydian" ), { 0, 1, 3, 5, 7, 8, 10, -1 } }, - { ChordCreator::tr( "Lydian" ), { 0, 2, 4, 6, 7, 9, 11, -1 } }, - { ChordCreator::tr( "Mixolydian" ), { 0, 2, 4, 5, 7, 9, 10, -1 } }, - { ChordCreator::tr( "Aeolian" ), { 0, 2, 3, 5, 7, 8, 10, -1 } }, - { ChordCreator::tr( "Locrian" ), { 0, 1, 3, 5, 6, 8, 10, -1 } }, - - { "", { -1, -1 } } + { "13" , { 0, 4, 7, 10, 14, 21, -1 } }, + { "13#9" , { 0, 4, 7, 10, 15, 21, -1 } }, + { "13b9" , { 0, 4, 7, 10, 13, 21, -1 } }, + { "13b5b9" , { 0, 4, 6, 10, 13, 21, -1 } }, + { "Maj13" , { 0, 4, 7, 11, 14, 21, -1 } }, + { "m13" , { 0, 3, 7, 10, 14, 21, -1 } }, + { "m-Maj13" , { 0, 3, 7, 11, 14, 21, -1 } }, + { "Major" , { 0, 2, 4, 5, 7, 9, 11, -1 } }, + { "Harmonic minor" , { 0, 2, 3, 5, 7, 8, 11, -1 } }, + { "Melodic minor" , { 0, 2, 3, 5, 7, 9, 11, -1 } }, + { "Whole tone" , { 0, 2, 4, 6, 8, 10, -1 } }, + { "Diminished" , { 0, 2, 3, 5, 6, 8, 9, 11, -1 } }, + { "Major pentatonic" , { 0, 2, 4, 7, 10, -1 } }, + { "Minor pentatonic" , { 0, 3, 5, 7, 10, -1 } }, + { "Jap in sen" , { 0, 1, 5, 7, 10, -1 } }, + { "Major bebop" , { 0, 2, 4, 5, 7, 8, 9, 11, -1 } }, + { "Dominant bebop" , { 0, 2, 4, 5, 7, 9, 10, 11, -1 } }, + { "Blues" , { 0, 3, 5, 6, 7, 10, -1 } }, + { "Arabic" , { 0, 1, 4, 5, 7, 8, 11, -1 } }, + { "Enigmatic" , { 0, 1, 4, 6, 8, 10, 11, -1 } }, + { "Neopolitan" , { 0, 1, 3, 5, 7, 9, 11, -1 } }, + { "Neopolitan minor" , { 0, 1, 3, 5, 7, 8, 11, -1 } }, + { "Hungarian minor" , { 0, 2, 3, 6, 7, 8, 11, -1 } }, + { "Dorian" , { 0, 2, 3, 5, 7, 9, 10, -1 } }, + { "Phrygolydian" , { 0, 1, 3, 5, 7, 8, 10, -1 } }, + { "Lydian" , { 0, 2, 4, 6, 7, 9, 11, -1 } }, + { "Mixolydian" , { 0, 2, 4, 5, 7, 9, 10, -1 } }, + { "Aeolian" , { 0, 2, 3, 5, 7, 8, 10, -1 } }, + { "Locrian" , { 0, 1, 3, 5, 6, 8, 10, -1 } }, } ; + + +ChordCreator::Chord::Chord( const char * n, const ChordSemiTones & semi_tones ) : + m_name( tr( n ) ) +{ + for( m_size = 0; m_size < MAX_CHORD_POLYPHONY; m_size++ ) + { + if( semi_tones[m_size] == -1 ) + { + break; + } + + m_semiTones[m_size] = semi_tones[m_size]; + } +} + + + + +bool ChordCreator::Chord::hasSemiTone( Sint8 semi_tone ) const +{ + for( int i = 0; i < size(); ++i ) + { + if( semi_tone == m_semiTones[i] ) + return true; + } + return false; +} + + + + +ChordCreator::ChordTable::ChordTable() : + QVector() +{ + for( int i = 0; + i < static_cast( sizeof s_initTable / sizeof *s_initTable ); + i++ ) + { + push_back( Chord( s_initTable[i].m_name, s_initTable[i].m_semiTones ) ); + } +} + + + + +const ChordCreator::Chord & ChordCreator::ChordTable::getByName( const QString & name, bool is_scale ) const +{ + for( int i = 0; i < size(); i++ ) + { + if( at( i ).getName() == name && is_scale == at( i ).isScale() ) + return at( i ); + } + + static Chord empty; + return empty; +} + + + + ChordCreator::ChordCreator( Model * _parent ) : Model( _parent, tr( "Chords" ) ), m_chordsEnabledModel( false, this ), m_chordsModel( this, tr( "Chord type" ) ), m_chordRangeModel( 1.0f, 1.0f, 9.0f, 1.0f, this, tr( "Chord range" ) ) { - for( int i = 0; s_chordTable[i].interval[0] != -1; ++i ) + const ChordTable & chord_table = ChordTable::getInstance(); + for( int i = 0; i < chord_table.size(); ++i ) { - m_chordsModel.addItem( tr( s_chordTable[i].name.toUtf8(). + m_chordsModel.addItem( tr( chord_table[i].getName().toUtf8(). constData() ) ); } } @@ -162,6 +221,7 @@ ChordCreator::~ChordCreator() void ChordCreator::processNote( notePlayHandle * _n ) { const int base_note_key = _n->key(); + const ChordTable & chord_table = ChordTable::getInstance(); // we add chord-subnotes to note if either note is a base-note and // arpeggio is not used or note is part of an arpeggio // at the same time we only add sub-notes if nothing of the note was @@ -186,13 +246,13 @@ void ChordCreator::processNote( notePlayHandle * _n ) // create it in the following loop, then we loop until // there's a -1 in the interval-array for( int i = ( octave_cnt == 0 ) ? 1 : 0; - s_chordTable[selected_chord].interval[i] != -1; + i < chord_table[selected_chord].size(); ++i ) { // add interval to sub-note-key const int sub_note_key = sub_note_key_base + - (int) s_chordTable[ - selected_chord].interval[i]; + (int) chord_table[ + selected_chord][i]; // maybe we're out of range -> let's get outta // here! if( sub_note_key > NumKeys ) @@ -255,11 +315,11 @@ Arpeggiator::Arpeggiator( Model * _parent ) : m_arpDirectionModel( this, tr( "Arpeggio direction" ) ), m_arpModeModel( this, tr( "Arpeggio mode" ) ) { - for( int i = 0; ChordCreator::s_chordTable[i].interval[0] != -1; ++i ) + const ChordCreator::ChordTable & chord_table = ChordCreator::ChordTable::getInstance(); + for( int i = 0; i < chord_table[i].size(); ++i ) { m_arpModel.addItem( ChordCreator::tr( - ChordCreator::s_chordTable[i]. - name.toUtf8().constData() ) ); + chord_table[i].getName().toUtf8().constData() ) ); } m_arpDirectionModel.addItem( tr( "Up" ), new PixmapLoader( "arp_up" ) ); @@ -314,8 +374,8 @@ void Arpeggiator::processNote( notePlayHandle * _n ) } } - const int cur_chord_size = ChordCreator::getChordSize( - ChordCreator::s_chordTable[selected_arp] ); + const ChordCreator::ChordTable & chord_table = ChordCreator::ChordTable::getInstance(); + const int cur_chord_size = chord_table[selected_arp].size(); const int range = (int)( cur_chord_size * m_arpRangeModel.value() ); const int total_range = range * cnphv.size(); @@ -401,8 +461,7 @@ void Arpeggiator::processNote( notePlayHandle * _n ) const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) * KeysPerOctave + - ChordCreator::s_chordTable[selected_arp]. - interval[cur_arp_idx % cur_chord_size]; + chord_table[selected_arp][cur_arp_idx % cur_chord_size]; // range-checking if( sub_note_key >= NumKeys || diff --git a/src/gui/piano_roll.cpp b/src/gui/piano_roll.cpp index 3ad36cc4d..0674e53bb 100644 --- a/src/gui/piano_roll.cpp +++ b/src/gui/piano_roll.cpp @@ -138,7 +138,7 @@ const int DEFAULT_PR_PPT = KEY_LINE_HEIGHT * DefaultStepsPerTact; pianoRoll::pianoRoll() : m_nemStr( QVector() ), m_noteEditMenu( NULL ), - m_toneMarkerMenu( NULL ), + m_semiToneMarkerMenu( NULL ), m_zoomingModel(), m_quantizeModel(), m_noteLenModel(), @@ -187,30 +187,34 @@ pianoRoll::pianoRoll() : this, SLOT(changeNoteEditMode(int)) ); signalMapper = new QSignalMapper( this ); - m_toneMarkerMenu = new QMenu( this ); + m_semiToneMarkerMenu = new QMenu( this ); - QAction * act = new QAction( tr("Mark/unmark current tone"), this ); + QAction * act = new QAction( tr("Mark/unmark current semitone"), this ); connect( act, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - signalMapper->setMapping( act, static_cast( tmmeMarkCurrent ) ); - m_toneMarkerMenu->addAction( act ); + signalMapper->setMapping( act, static_cast( stmaMarkCurrentSemiTone ) ); + m_semiToneMarkerMenu->addAction( act ); - act = new QAction( tr("Mark minor scale"), this ); + act = new QAction( tr("Mark current scale"), this ); + act->setEnabled( false ); connect( act, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - signalMapper->setMapping( act, static_cast( tmmeMarkMinorScale ) ); - m_toneMarkerMenu->addAction( act ); + connect( this, SIGNAL(semiToneMarkerMenuScaleSetEnabled(bool)), act, SLOT(setEnabled(bool)) ); + signalMapper->setMapping( act, static_cast( stmaMarkCurrentScale ) ); + m_semiToneMarkerMenu->addAction( act ); - act = new QAction( tr("Mark major scale"), this ); + act = new QAction( tr("Mark current chord"), this ); + act->setEnabled( false ); connect( act, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - signalMapper->setMapping( act, static_cast( tmmeMarkMajorScale ) ); - m_toneMarkerMenu->addAction( act ); + connect( this, SIGNAL(semiToneMarkerMenuChordSetEnabled(bool)), act, SLOT(setEnabled(bool)) ); + signalMapper->setMapping( act, static_cast( stmaMarkCurrentChord ) ); + m_semiToneMarkerMenu->addAction( act ); act = new QAction( tr("Unmark all"), this ); connect( act, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - signalMapper->setMapping( act, static_cast( tmmeUnmarkAll ) ); - m_toneMarkerMenu->addAction( act ); + signalMapper->setMapping( act, static_cast( stmaUnmarkAll ) ); + m_semiToneMarkerMenu->addAction( act ); connect( signalMapper, SIGNAL(mapped(int)), - this, SLOT(markTone(int)) ); + this, SLOT(markSemiTone(int)) ); // init pixmaps if( s_whiteKeySmallPm == NULL ) @@ -498,6 +502,52 @@ pianoRoll::pianoRoll() : this, SLOT( quantizeChanged() ) ); + const ChordCreator::ChordTable & chord_table = ChordCreator::ChordTable::getInstance(); + + // setup scale-stuff + QLabel * scale_lbl = new QLabel( m_toolBar ); + scale_lbl->setPixmap( embed::getIconPixmap( "scale" ) ); + + m_scaleModel.addItem( tr("No scale") ); + for( int i = 0; i < chord_table.size(); ++i ) + { + if( chord_table[i].isScale() ) + { + m_scaleModel.addItem( chord_table[i].getName() ); + } + } + + m_scaleModel.setValue( 0 ); + m_scaleComboBox = new comboBox( m_toolBar ); + m_scaleComboBox->setModel( &m_scaleModel ); + m_scaleComboBox->setFixedSize( 105, 22 ); + // change can update m_semiToneMarkerMenu + connect( &m_scaleModel, SIGNAL( dataChanged() ), + this, SLOT( updateSemiToneMarkerMenu() ) ); + + + // setup chord-stuff + QLabel * chord_lbl = new QLabel( m_toolBar ); + chord_lbl->setPixmap( embed::getIconPixmap( "chord" ) ); + + m_chordModel.addItem( tr("No chord") ); + for( int i = 0; i < chord_table.size(); ++i ) + { + if( ! chord_table[i].isScale() ) + { + m_chordModel.addItem( chord_table[i].getName() ); + } + } + + m_chordModel.setValue( 0 ); + m_chordComboBox = new comboBox( m_toolBar ); + m_chordComboBox->setModel( &m_chordModel ); + m_chordComboBox->setFixedSize( 105, 22 ); + // change can update m_semiToneMarkerMenu + connect( &m_chordModel, SIGNAL( dataChanged() ), + this, SLOT( updateSemiToneMarkerMenu() ) ); + + tb_layout->addSpacing( 5 ); tb_layout->addWidget( m_playButton ); tb_layout->addWidget( m_recordButton ); @@ -526,6 +576,14 @@ pianoRoll::pianoRoll() : tb_layout->addWidget( note_len_lbl ); tb_layout->addSpacing( 4 ); tb_layout->addWidget( m_noteLenComboBox ); + tb_layout->addSpacing( 10 ); + tb_layout->addWidget( scale_lbl ); + tb_layout->addSpacing( 4 ); + tb_layout->addWidget( m_scaleComboBox ); + tb_layout->addSpacing( 10 ); + tb_layout->addWidget( chord_lbl ); + tb_layout->addSpacing( 4 ); + tb_layout->addWidget( m_chordComboBox ); tb_layout->addStretch(); // setup our actual window @@ -564,60 +622,69 @@ void pianoRoll::changeNoteEditMode( int i ) } -void pianoRoll::markTone( int i ) +void pianoRoll::markSemiTone( int i ) { - static const int major_scale[KeysPerOctave] = { - 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 - }; + const int key = getKey( mapFromGlobal( m_semiToneMarkerMenu->pos() ).y() ); + const ChordCreator::Chord * chord = 0; - static const int minor_scale[KeysPerOctave] = { - 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0 - }; - - const int * scale = 0; - const int key = getKey( mapFromGlobal( m_toneMarkerMenu->pos() ).y() ); - - switch( static_cast( i ) ) + switch( static_cast( i ) ) { - case tmmeUnmarkAll: - m_markedTones.clear(); + case stmaUnmarkAll: + m_markedSemiTones.clear(); break; - case tmmeMarkCurrent: + case stmaMarkCurrentSemiTone: { - QList::iterator i = qFind( m_markedTones.begin(), m_markedTones.end(), key ); - if( i != m_markedTones.end() ) + QList::iterator i = qFind( m_markedSemiTones.begin(), m_markedSemiTones.end(), key ); + if( i != m_markedSemiTones.end() ) { - m_markedTones.erase( i ); + m_markedSemiTones.erase( i ); } else { - m_markedTones.push_back( key ); + m_markedSemiTones.push_back( key ); } break; } - case tmmeMarkMinorScale: - scale = minor_scale; - case tmmeMarkMajorScale: - if( ! scale ) + case stmaMarkCurrentScale: + chord = & ChordCreator::ChordTable::getInstance() + .getScaleByName( m_scaleModel.currentText() ); + case stmaMarkCurrentChord: + { + if( ! chord ) { - scale = major_scale; + chord = & ChordCreator::ChordTable::getInstance() + .getChordByName( m_chordModel.currentText() ); } - for( int i = 0; i <= NumKeys; i++ ) + if( chord->isEmpty() ) { - if( scale[std::abs( key - i ) % KeysPerOctave] ) + break; + } + else if( chord->isScale() ) + { + m_markedSemiTones.clear(); + } + + const int first = chord->isScale() ? 0 : key; + const int last = chord->isScale() ? NumKeys : key + chord->last(); + const int cap = chord->isScale() ? KeysPerOctave : chord->last(); + + for( int i = first; i <= last; i++ ) + { + if( chord->hasSemiTone( std::abs( key - i ) % cap ) ) { - m_markedTones.push_back( i ); + m_markedSemiTones.push_back( i ); } } break; + } default: ; } - qSort( m_markedTones.begin(), m_markedTones.end(), qGreater() ); - QList::iterator new_end = std::unique( m_markedTones.begin(), m_markedTones.end() ); - m_markedTones.erase( new_end, m_markedTones.end() ); + qSort( m_markedSemiTones.begin(), m_markedSemiTones.end(), qGreater() ); + QList::iterator new_end = std::unique( m_markedSemiTones.begin(), m_markedSemiTones.end() ); + m_markedSemiTones.erase( new_end, m_markedSemiTones.end() ); } @@ -1453,11 +1520,15 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) else if( _me->button() == Qt::LeftButton && m_editMode == ModeDraw ) { + // whether this action creates new note(s) or not + bool is_new_note = false; + note * created_new_note = NULL; // did it reach end of vector because // there's no note?? if( it == notes.begin()-1 ) { + is_new_note = true; m_pattern->setType( pattern::MelodyPattern ); // then set new note @@ -1470,13 +1541,35 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) // because live notes should still be quantized at the half. midiTime note_pos( pos_ticks - ( quantization() / 2 ) ); midiTime note_len( newNoteLen() ); - + note new_note( note_len, note_pos, key_num ); new_note.setSelected( true ); new_note.setPanning( m_lastNotePanning ); new_note.setVolume( m_lastNoteVolume ); created_new_note = m_pattern->addNote( new_note ); + const ChordCreator::Chord & chord = ChordCreator::ChordTable::getInstance() + .getChordByName( m_chordModel.currentText() ); + + if( ! chord.isEmpty() ) + { + // if a chord is selected, create following notes in chord + // or arpeggio mode + const bool arpeggio = _me->modifiers() & Qt::ShiftModifier; + for( int i = 1; i < chord.size(); i++ ) + { + if( arpeggio ) + { + note_pos += note_len; + } + note new_note( note_len, note_pos, key_num + chord[i] ); + new_note.setSelected( true ); + new_note.setPanning( m_lastNotePanning ); + new_note.setVolume( m_lastNoteVolume ); + m_pattern->addNote( new_note ); + } + } + // reset it so that it can be used for // ops (move, resize) after this // code-block @@ -1579,7 +1672,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) // if they're holding shift, copy all selected notes if( //*it != created_new_note && - _me->modifiers() & Qt::ShiftModifier ) + ! is_new_note && _me->modifiers() & Qt::ShiftModifier ) { // vector to hold new notes until we're through the loop QVector newNotes; @@ -1662,7 +1755,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) if( _me->buttons() == Qt::RightButton ) { // right click, tone marker contextual menu - m_toneMarkerMenu->popup( mapToGlobal( QPoint( _me->x(), _me->y() ) ) ); + m_semiToneMarkerMenu->popup( mapToGlobal( QPoint( _me->x(), _me->y() ) ) ); } else { @@ -2703,9 +2796,9 @@ void pianoRoll::paintEvent( QPaintEvent * _pe ) } // display note marks - for( int i = 0; i < m_markedTones.size(); i++ ) + for( int i = 0; i < m_markedSemiTones.size(); i++ ) { - const int key_num = m_markedTones.at( i ); + const int key_num = m_markedSemiTones.at( i ); const int y = keyAreaBottom() + 5 - KEY_LINE_HEIGHT * ( key_num - m_startKey + 1 ); @@ -3673,6 +3766,21 @@ int pianoRoll::quantization() const +void pianoRoll::updateSemiToneMarkerMenu() +{ + const ChordCreator::Chord & scale = ChordCreator::ChordTable::getInstance() + .getScaleByName( m_scaleModel.currentText() ); + + const ChordCreator::Chord & chord = ChordCreator::ChordTable::getInstance() + .getChordByName( m_chordModel.currentText() ); + + emit semiToneMarkerMenuScaleSetEnabled( ! scale.isEmpty() ); + emit semiToneMarkerMenuChordSetEnabled( ! chord.isEmpty() ); +} + + + + midiTime pianoRoll::newNoteLen() const { if( m_noteLenModel.value() == 0 )