diff --git a/data/hydrogen.default.conf b/data/hydrogen.default.conf
index a6dedfba4..c9833a240 100644
--- a/data/hydrogen.default.conf
+++ b/data/hydrogen.default.conf
@@ -104,9 +104,10 @@
1.1
8
+ 4
+ 4
21
3
- false
true
diff --git a/data/img/gray/patternEditor/background_res-new.png b/data/img/gray/patternEditor/background_res-new.png
index 7480eaa3b..165f9dc2c 100644
Binary files a/data/img/gray/patternEditor/background_res-new.png and b/data/img/gray/patternEditor/background_res-new.png differ
diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp
index 5a02a2331..86d7d8b6e 100644
--- a/src/core/AudioEngine.cpp
+++ b/src/core/AudioEngine.cpp
@@ -117,7 +117,7 @@ AudioEngine::AudioEngine()
, m_nPatternTickPosition( 0 )
, m_nSongSizeInTicks( 0 )
, m_nRealtimeFrames( 0 )
- , m_nAddRealtimeNoteTickPosition( 0 )
+ , m_fAddRealtimeNoteTickPosition( 0. )
, m_nSongPos( -1 )
, m_nSelectedPatternNumber( 0 )
{
@@ -932,7 +932,7 @@ inline void AudioEngine::processPlayNotes( unsigned long nframes )
float velocity_adjustment = 1.0f;
if ( pSong->getMode() == Song::SONG_MODE ) {
- float fPos = m_nSongPos + (pNote->get_position()%192) / 192.f;
+ float fPos = m_nSongPos + ( ( (int) pNote->get_position() ) %192) / 192.f; //TODO what if pattern length > 192?
velocity_adjustment = vp->get_value(fPos);
}
@@ -1713,10 +1713,14 @@ inline int AudioEngine::updateNoteQueue( unsigned nFrames )
// iterator (notes won't be altered!). After some
// humanization was applied to onset of each note, it
// will be added to `m_songNoteQueue` for playback.
- FOREACH_NOTE_CST_IT_BOUND(notes,it,m_nPatternTickPosition) {
+ //FOREACH_NOTE_CST_IT_BOUND(notes,it,m_nPatternTickPosition) { //TODO macro
+ for( Pattern::notes_cst_it_t it=notes->lower_bound( m_nPatternTickPosition );
+ it != notes->end() && it->first < m_nPatternTickPosition + 1;
+ it++ ) {
Note *pNote = it->second;
if ( pNote ) {
pNote->set_just_recorded( false );
+
int nOffset = 0;
// Swing //
@@ -1761,9 +1765,10 @@ inline int AudioEngine::updateNoteQueue( unsigned nFrames )
// it the new offset, and push it to the list
// of all notes, which are about to be played
// back.
- // TODO: Why a copy?
+ // Why a copy? because it has the new offset (including swing and random timing) in its
+ // humanized delay, and tick position is expressed referring to start time (and not pattern).
Note *pCopiedNote = new Note( pNote );
- pCopiedNote->set_position( tick );
+ pCopiedNote->set_position( tick + pNote->get_position() - floor( pNote->get_position() ) );
pCopiedNote->set_humanize_delay( nOffset );
pNote->get_instrument()->enqueue();
m_songNoteQueue.push( pCopiedNote );
diff --git a/src/core/AudioEngine.h b/src/core/AudioEngine.h
index b9c6a6e68..d7e080303 100644
--- a/src/core/AudioEngine.h
+++ b/src/core/AudioEngine.h
@@ -704,8 +704,8 @@ class AudioEngine : public H2Core::Object
unsigned long getRealtimeFrames() const;
void setRealtimeFrames( unsigned long nFrames );
- unsigned int getAddRealtimeNoteTickPosition() const;
- void setAddRealtimeNoteTickPosition( unsigned int tickPosition );
+ double getAddRealtimeNoteTickPosition() const;
+ void setAddRealtimeNoteTickPosition( double fTickPosition );
struct timeval& getCurrentTickTime();
@@ -952,7 +952,7 @@ class AudioEngine : public H2Core::Object
* position TransportInfo::m_nFrames.
*/
unsigned long m_nRealtimeFrames;
- unsigned int m_nAddRealtimeNoteTickPosition;
+ double m_fAddRealtimeNoteTickPosition;
/**
* Current state of the H2Core::AudioEngine.
@@ -1145,12 +1145,12 @@ inline void AudioEngine::setRealtimeFrames( unsigned long nFrames ) {
m_nRealtimeFrames = nFrames;
}
-inline unsigned int AudioEngine::getAddRealtimeNoteTickPosition() const {
- return m_nAddRealtimeNoteTickPosition;
+inline double AudioEngine::getAddRealtimeNoteTickPosition() const {
+ return m_fAddRealtimeNoteTickPosition;
}
-inline void AudioEngine::setAddRealtimeNoteTickPosition( unsigned int tickPosition) {
- m_nAddRealtimeNoteTickPosition = tickPosition;
+inline void AudioEngine::setAddRealtimeNoteTickPosition( double fTickPosition) {
+ m_fAddRealtimeNoteTickPosition = fTickPosition;
}
};
diff --git a/src/core/Basics/Note.cpp b/src/core/Basics/Note.cpp
index 3a306f7dc..7e8438d4f 100644
--- a/src/core/Basics/Note.cpp
+++ b/src/core/Basics/Note.cpp
@@ -37,7 +37,7 @@ namespace H2Core
const char* Note::__class_name = "Note";
const char* Note::__key_str[] = { "C", "Cs", "D", "Ef", "E", "F", "Fs", "G", "Af", "A", "Bf", "B" };
-Note::Note( Instrument* instrument, int position, float velocity, float pan_l, float pan_r, int length, float pitch )
+Note::Note( Instrument* instrument, double position, float velocity, float pan_l, float pan_r, int length, float pitch )
: Object( __class_name ),
__instrument( instrument ),
__instrument_id( 0 ),
@@ -212,14 +212,14 @@ void Note::dump()
void Note::save_to( XMLNode* node )
{
- node->write_int( "position", __position );
+ node->write_double( "position", __position );
node->write_float( "leadlag", __lead_lag );
node->write_float( "velocity", __velocity );
node->write_float( "pan_L", __pan_l );
node->write_float( "pan_R", __pan_r );
node->write_float( "pitch", __pitch );
node->write_string( "key", key_to_string() );
- node->write_int( "length", __length );
+ node->write_double( "length", __length );
node->write_int( "instrument", get_instrument()->get_id() );
node->write_bool( "note_off", __note_off );
node->write_float( "probability", __probability );
@@ -229,11 +229,11 @@ Note* Note::load_from( XMLNode* node, InstrumentList* instruments )
{
Note* note = new Note(
nullptr,
- node->read_int( "position", 0 ),
+ node->read_double( "position", 0 ),
node->read_float( "velocity", 0.8f ),
node->read_float( "pan_L", 0.5f ),
node->read_float( "pan_R", 0.5f ),
- node->read_int( "length", -1 ),
+ node->read_double( "length", -1 ),
node->read_float( "pitch", 0.0f )
);
note->set_lead_lag( node->read_float( "leadlag", 0, false, false ) );
@@ -253,11 +253,11 @@ QString Note::toQString( const QString& sPrefix, bool bShort ) const {
sOutput = QString( "%1[Note]\n" ).arg( sPrefix )
.append( QString( "%1%2instrument_id: %3\n" ).arg( sPrefix ).arg( s ).arg( __instrument_id ) )
.append( QString( "%1%2specific_compo_id: %3\n" ).arg( sPrefix ).arg( s ).arg( __specific_compo_id ) )
- .append( QString( "%1%2position: %3\n" ).arg( sPrefix ).arg( s ).arg( __position ) )
+ .append( QString( "%1%2position: %3\n" ).arg( sPrefix ).arg( s ).arg( __position, 0, 'g', 10 ) ) //TODO what precision?
.append( QString( "%1%2velocity: %3\n" ).arg( sPrefix ).arg( s ).arg( __velocity ) )
.append( QString( "%1%2pan_l: %3\n" ).arg( sPrefix ).arg( s ).arg( __pan_l ) )
.append( QString( "%1%2pan_r: %3\n" ).arg( sPrefix ).arg( s ).arg( __pan_r ) )
- .append( QString( "%1%2length: %3\n" ).arg( sPrefix ).arg( s ).arg( __length ) )
+ .append( QString( "%1%2length: %3\n" ).arg( sPrefix ).arg( s ).arg( __length, 0, 'g', 10 ) ) //TODO what precision?
.append( QString( "%1%2pitch: %3\n" ).arg( sPrefix ).arg( s ).arg( __pitch ) )
.append( QString( "%1%2key: %3\n" ).arg( sPrefix ).arg( s ).arg( __key ) )
.append( QString( "%1%2octave: %3\n" ).arg( sPrefix ).arg( s ).arg( __octave ) )
@@ -291,11 +291,11 @@ QString Note::toQString( const QString& sPrefix, bool bShort ) const {
sOutput = QString( "[Note]" )
.append( QString( ", instrument_id: %1" ).arg( __instrument_id ) )
.append( QString( ", specific_compo_id: %1" ).arg( __specific_compo_id ) )
- .append( QString( ", position: %1" ).arg( __position ) )
+ .append( QString( ", position: %1" ).arg( __position, 0, 'g', 10 ) ) //TODO what precision?
.append( QString( ", velocity: %1" ).arg( __velocity ) )
.append( QString( ", pan_l: %1" ).arg( __pan_l ) )
.append( QString( ", pan_r: %1" ).arg( __pan_r ) )
- .append( QString( ", length: %1" ).arg( __length ) )
+ .append( QString( ", length: %1" ).arg( __length, 0, 'g', 10 ) ) //TODO what precision?
.append( QString( ", pitch: %1" ).arg( __pitch ) )
.append( QString( ", key: %1" ).arg( __key ) )
.append( QString( ", octave: %1" ).arg( __octave ) )
diff --git a/src/core/Basics/Note.h b/src/core/Basics/Note.h
index 6492cfcb6..b37a849cc 100644
--- a/src/core/Basics/Note.h
+++ b/src/core/Basics/Note.h
@@ -82,7 +82,7 @@ class Note : public H2Core::Object
* \param length it's length
* \param pitch it's pitch
*/
- Note( Instrument* instrument, int position, float velocity, float pan_l, float pan_r, int length, float pitch );
+ Note( Instrument* instrument, double position, float velocity, float pan_l, float pan_r, int length, float pitch );
/**
* copy constructor with an optional parameter
@@ -136,9 +136,9 @@ class Note : public H2Core::Object
* #__position setter
* \param value the new value
*/
- void set_position( int value );
+ void set_position( double value );
/** #__position accessor */
- int get_position() const;
+ double get_position() const;
/**
* #__velocity setter
* \param value the new value
@@ -171,9 +171,9 @@ class Note : public H2Core::Object
* #__length setter
* \param value the new value
*/
- void set_length( int value );
+ void set_length( double value );
/** #__length accessor */
- int get_length() const;
+ double get_length() const;
/**
* #__pitch setter
* \param value the new value
@@ -299,6 +299,7 @@ class Note : public H2Core::Object
* \param val_r the right channel value
*/
void compute_lr_values( float* val_l, float* val_r );
+
/** Formatted string version for debugging purposes.
* \param sPrefix String prefix which will be added in front of
* every new line
@@ -313,11 +314,11 @@ class Note : public H2Core::Object
Instrument* __instrument; ///< the instrument to be played by this note
int __instrument_id; ///< the id of the instrument played by this note
int __specific_compo_id; ///< play a specific component, -1 if playing all
- int __position; ///< note position inside the pattern
+ double __position; ///< note position inside the pattern, in ticks
float __velocity; ///< velocity (intensity) of the note [0;1]
float __pan_l; ///< pan of the note (left volume) [0;0.5]
float __pan_r; ///< pan of the note (right volume) [0;0.5]
- int __length; ///< the length of the note
+ double __length; ///< the length of the note
float __pitch; ///< the frequency of the note
Key __key; ///< the key, [0;11]==[C;B]
Octave __octave; ///< the octave [-3;3]
@@ -376,12 +377,12 @@ inline int Note::get_specific_compo_id() const
return __specific_compo_id;
}
-inline void Note::set_position( int value )
+inline void Note::set_position( double value )
{
__position = value;
}
-inline int Note::get_position() const
+inline double Note::get_position() const
{
return __position;
}
@@ -406,12 +407,12 @@ inline float Note::get_lead_lag() const
return __lead_lag;
}
-inline void Note::set_length( int value )
+inline void Note::set_length( double value )
{
__length = value;
}
-inline int Note::get_length() const
+inline double Note::get_length() const
{
return __length;
}
@@ -593,6 +594,7 @@ inline void Note::compute_lr_values( float* val_l, float* val_r )
};
+
#endif // H2C_NOTE_H
/* vim: set softtabstop=4 noexpandtab: */
diff --git a/src/core/Basics/Pattern.cpp b/src/core/Basics/Pattern.cpp
index ac01714a9..21f9c80e2 100644
--- a/src/core/Basics/Pattern.cpp
+++ b/src/core/Basics/Pattern.cpp
@@ -151,9 +151,9 @@ void Pattern::save_to( XMLNode* node, const Instrument* instrumentOnly ) const
}
}
-Note* Pattern::find_note( int idx_a, int idx_b, Instrument* instrument, Note::Key key, Note::Octave octave, bool strict ) const
+Note* Pattern::find_note( double idx_a, int idx_b, Instrument* instrument, Note::Key key, Note::Octave octave, bool strict ) const
{
- for( notes_cst_it_t it=__notes.lower_bound( idx_a ); it!=__notes.upper_bound( idx_a ); it++ ) {
+ for( notes_cst_it_t it=__notes.lower_bound( idx_a - POS_EPSILON ) ; it!=__notes.upper_bound( idx_a + POS_EPSILON ); it++ ) {
Note* note = it->second;
assert( note );
if ( note->match( instrument, key, octave ) ) return note;
@@ -176,10 +176,10 @@ Note* Pattern::find_note( int idx_a, int idx_b, Instrument* instrument, Note::Ke
return nullptr;
}
-Note* Pattern::find_note( int idx_a, int idx_b, Instrument* instrument, bool strict ) const
+Note* Pattern::find_note( double idx_a, int idx_b, Instrument* instrument, bool strict ) const
{
notes_cst_it_t it;
- for( it=__notes.lower_bound( idx_a ); it!=__notes.upper_bound( idx_a ); it++ ) {
+ for( it=__notes.lower_bound( idx_a - POS_EPSILON ); it!=__notes.upper_bound( idx_a + POS_EPSILON ); it++ ) {
Note* note = it->second;
assert( note );
if ( note->get_instrument() == instrument ) return note;
@@ -205,8 +205,8 @@ Note* Pattern::find_note( int idx_a, int idx_b, Instrument* instrument, bool str
void Pattern::remove_note( Note* note )
{
- int pos = note->get_position();
- for( notes_it_t it=__notes.lower_bound( pos ); it!=__notes.end() && it->first == pos; ++it ) {
+ double fPos = note->get_position();
+ FOREACH_NOTE_IT_BOUND( &__notes, it, fPos ) {
if( it->second==note ) {
__notes.erase( it );
break;
diff --git a/src/core/Basics/Pattern.h b/src/core/Basics/Pattern.h
index 2fc3ed2dd..798b56bc2 100644
--- a/src/core/Basics/Pattern.h
+++ b/src/core/Basics/Pattern.h
@@ -43,7 +43,7 @@ class Pattern : public H2Core::Object
H2_OBJECT
public:
///< multimap note type
- typedef std::multimap notes_t;
+ typedef std::multimap notes_t;
///< multimap note iterator type
typedef notes_t::iterator notes_it_t;
///< multimap note const iterator type
@@ -125,7 +125,7 @@ class Pattern : public H2Core::Object
* \param strict if set to false, will search for a note around the given idx
* \return the note if found, 0 otherwise
*/
- Note* find_note( int idx_a, int idx_b, Instrument* instrument, bool strict=true ) const;
+ Note* find_note( double idx_a, int idx_b, Instrument* instrument, bool strict=true ) const; // TODO second arg float
/**
* search for a note at a given index within __notes which correspond to the given arguments
* \param idx_a the first __notes index to search in
@@ -136,7 +136,7 @@ class Pattern : public H2Core::Object
* \param strict if set to false, will search for a note around the given idx
* \return the note if found, 0 otherwise
*/
- Note* find_note( int idx_a, int idx_b, Instrument* instrument, Note::Key key, Note::Octave octave, bool strict=true) const;
+ Note* find_note( double idx_a, int idx_b, Instrument* instrument, Note::Key key, Note::Octave octave, bool strict=true) const; // TODO second arg float
/**
* removes a given note from __notes, it's not deleted
* \param note the note to be removed
@@ -222,15 +222,17 @@ class Pattern : public H2Core::Object
#define FOREACH_NOTE_CST_IT_BEGIN_END(_notes,_it) \
for( Pattern::notes_cst_it_t _it=(_notes)->begin(); (_it)!=(_notes)->end(); (_it)++ )
+
+#define POS_EPSILON 0.0001 // TODO choose value. use as argument of macro?
#define FOREACH_NOTE_CST_IT_BOUND(_notes,_it,_bound) \
- for( Pattern::notes_cst_it_t _it=(_notes)->lower_bound((_bound)); (_it)!=(_notes)->end() && (_it)->first == (_bound); (_it)++ )
+ for( Pattern::notes_cst_it_t _it=(_notes)->lower_bound((_bound) - POS_EPSILON ); (_it)!=(_notes)->end() && (_it)->first < (_bound) + POS_EPSILON; (_it)++ )
#define FOREACH_NOTE_IT_BEGIN_END(_notes,_it) \
for( Pattern::notes_it_t _it=(_notes)->begin(); (_it)!=(_notes)->end(); (_it)++ )
#define FOREACH_NOTE_IT_BOUND(_notes,_it,_bound) \
- for( Pattern::notes_it_t _it=(_notes)->lower_bound((_bound)); (_it)!=(_notes)->end() && (_it)->first == (_bound); (_it)++ )
+ for( Pattern::notes_it_t _it=(_notes)->lower_bound((_bound) - POS_EPSILON ); (_it)!=(_notes)->end() && (_it)->first < (_bound) + POS_EPSILON; (_it)++ )
// DEFINITIONS
diff --git a/src/core/Basics/Song.cpp b/src/core/Basics/Song.cpp
index 666f4a356..619a6926e 100644
--- a/src/core/Basics/Song.cpp
+++ b/src/core/Basics/Song.cpp
@@ -1531,7 +1531,7 @@ Pattern* SongReader::getPattern( QDomNode pattern, InstrumentList* pInstrList )
Note* pNote = nullptr;
- unsigned nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 );
+ double fPosition = LocalFileMng::readXmlDouble( noteNode, "position", 0 );
float fLeadLag = LocalFileMng::readXmlFloat( noteNode, "leadlag", 0.0, false, false );
float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f );
float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 );
@@ -1558,7 +1558,7 @@ Pattern* SongReader::getPattern( QDomNode pattern, InstrumentList* pInstrList )
noteoff = true;
}
- pNote = new Note( pInstrumentRef, nPosition, fVelocity, fPan_L, fPan_R, nLength, nPitch );
+ pNote = new Note( pInstrumentRef, fPosition, fVelocity, fPan_L, fPan_R, nLength, nPitch );
pNote->set_key_octave( sKey );
pNote->set_lead_lag( fLeadLag );
pNote->set_note_off( noteoff );
@@ -1582,7 +1582,7 @@ Pattern* SongReader::getPattern( QDomNode pattern, InstrumentList* pInstrList )
Note* pNote = nullptr;
- unsigned nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 );
+ double fPosition = LocalFileMng::readXmlDouble( noteNode, "position", 0 );
float fLeadLag = LocalFileMng::readXmlFloat( noteNode, "leadlag", 0.0, false, false );
float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f );
float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 );
@@ -1595,7 +1595,7 @@ Pattern* SongReader::getPattern( QDomNode pattern, InstrumentList* pInstrList )
Instrument* instrRef = pInstrList->find( instrId );
assert( instrRef );
- pNote = new Note( instrRef, nPosition, fVelocity, fPan_L, fPan_R, nLength, nPitch );
+ pNote = new Note( instrRef, fPosition, fVelocity, fPan_L, fPan_R, nLength, nPitch );
pNote->set_lead_lag( fLeadLag );
//infoLog( "new note!! pos: " + toString( pNote->m_nPosition ) + "\t instr: " + instrId );
diff --git a/src/core/EventQueue.h b/src/core/EventQueue.h
index caa653d25..6bb75de03 100644
--- a/src/core/EventQueue.h
+++ b/src/core/EventQueue.h
@@ -238,7 +238,7 @@ class EventQueue : public H2Core::Object
Event pop_event();
struct AddMidiNoteVector {
- int m_column; //position
+ double m_column; //position
int m_row; //instrument row
int m_pattern; // pattern number
int m_length;
diff --git a/src/core/Helpers/Xml.cpp b/src/core/Helpers/Xml.cpp
index 8b36c815e..ee5432a43 100644
--- a/src/core/Helpers/Xml.cpp
+++ b/src/core/Helpers/Xml.cpp
@@ -85,6 +85,17 @@ float XMLNode::read_float( const QString& node, float default_value, bool inexis
return c_locale.toFloat( ret );
}
+double XMLNode::read_double( const QString& node, double default_value, bool inexistent_ok, bool empty_ok )
+{
+ QString ret = read_child_node( node, inexistent_ok, empty_ok );
+ if( ret.isNull() ) {
+ DEBUGLOG( QString( "Using default value %1 for %2" ).arg( default_value ).arg( node ) );
+ return default_value;
+ }
+ QLocale c_locale = QLocale::c();
+ return c_locale.toDouble( ret );
+}
+
int XMLNode::read_int( const QString& node, int default_value, bool inexistent_ok, bool empty_ok )
{
QString ret = read_child_node( node, inexistent_ok, empty_ok );
@@ -156,6 +167,10 @@ void XMLNode::write_float( const QString& node, const float value )
{
write_child_node( node, QString::number( value ) );
}
+void XMLNode::write_double( const QString& node, const double value )
+{
+ write_child_node( node, QString( "%1" ).arg( value, 0, 'g', 10 ) );
+}
void XMLNode::write_int( const QString& node, const int value )
{
write_child_node( node, QString::number( value ) );
diff --git a/src/core/Helpers/Xml.h b/src/core/Helpers/Xml.h
index 088ca1fef..afa336467 100644
--- a/src/core/Helpers/Xml.h
+++ b/src/core/Helpers/Xml.h
@@ -52,6 +52,14 @@ class XMLNode : public H2Core::Object, public QDomNode
* \param empty_ok if set to false output a DEBUG log line if the child node is empty
*/
float read_float( const QString& node, float default_value, bool inexistent_ok=true, bool empty_ok=true );
+ /**
+ * reads a double stored into a child node
+ * \param node the name of the child node to read into
+ * \param default_value the value returned if something goes wrong
+ * \param inexistent_ok if set to false output a DEBUG log line if the node doesn't exists
+ * \param empty_ok if set to false output a DEBUG log line if the child node is empty
+ */
+ double read_double( const QString& node, double default_value, bool inexistent_ok=true, bool empty_ok=true );
/**
* reads a string stored into a child node
* \param node the name of the child node to read into
@@ -94,6 +102,12 @@ class XMLNode : public H2Core::Object, public QDomNode
* \param value the value to write
*/
void write_float( const QString& node, const float value );
+ /**
+ * write a double into a child node
+ * \param node the name of the child node to create
+ * \param value the value to write
+ */
+ void write_double( const QString& node, const double value );
/**
* write a string into a child node
* \param node the name of the child node to create
diff --git a/src/core/Hydrogen.cpp b/src/core/Hydrogen.cpp
index 6b9c96994..48928ff2a 100644
--- a/src/core/Hydrogen.cpp
+++ b/src/core/Hydrogen.cpp
@@ -350,9 +350,6 @@ void Hydrogen::addRealtimeNote( int instrument,
AudioEngine* pAudioEngine = m_pAudioEngine;
Preferences *pPreferences = Preferences::get_instance();
unsigned int nRealColumn = 0;
- unsigned res = pPreferences->getPatternEditorGridResolution();
- int nBase = pPreferences->isPatternEditorUsingTriplets() ? 3 : 4;
- int scalar = ( 4 * MAX_NOTES ) / ( res * nBase );
bool hearnote = forcePlay;
int currentPatternNumber;
@@ -367,7 +364,7 @@ void Hydrogen::addRealtimeNote( int instrument,
}
}
- // Get current partern and column, compensating for "lookahead" if required
+ // Get current pattern and column, compensating for "lookahead" if required
const Pattern* currentPattern = nullptr;
unsigned int column = 0;
float fTickSize = pAudioEngine->getAudioDriver()->m_transport.m_fTickSize;
@@ -459,18 +456,31 @@ void Hydrogen::addRealtimeNote( int instrument,
nRealColumn = getRealtimeTickPosition();
+ double fTickPosition;
if ( currentPattern && pPreferences->getQuantizeEvents() ) {
- // quantize it to scale
- unsigned qcolumn = ( unsigned )::round( column / ( double )scalar ) * scalar;
+ //double fScalar = ( 4 * MAX_NOTES ) / ( res * nBase );
+ int nResolution = pPreferences->getPatternEditorGridResolution();
+ int nTupletNumerator = pPreferences->getPatternEditorGridTupletNumerator();
+ int nTupletDenominator = pPreferences->getPatternEditorGridTupletDenominator();
+
+ // calculate the granularity = grid quantum length in ticks
+ double fGridQuantumInTicks = (double) MAX_NOTES * nTupletDenominator / ( nTupletNumerator * nResolution );
+
+ // get position of nearest grid mark
+ double fQuantizedColumn = round( column / fGridQuantumInTicks ) * fGridQuantumInTicks;
//we have to make sure that no beat is added on the last displayed note in a bar
//for example: if the pattern has 4 beats, the editor displays 5 beats, so we should avoid adding beats an note 5.
- if ( qcolumn == currentPattern->get_length() ) qcolumn = 0;
- column = qcolumn;
+ if ( fQuantizedColumn >= currentPattern->get_length() ) {
+ fTickPosition = 0.;
+ } else {
+ fTickPosition = fQuantizedColumn;
+ }
+ } else {
+ fTickPosition = column;
}
- unsigned position = column;
- pAudioEngine->setAddRealtimeNoteTickPosition( column );
+ pAudioEngine->setAddRealtimeNoteTickPosition( fTickPosition );
Instrument *instrRef = nullptr;
if ( pSong ) {
@@ -482,7 +492,7 @@ void Hydrogen::addRealtimeNote( int instrument,
assert( currentPattern );
if ( doRecord ) {
EventQueue::AddMidiNoteVector noteAction;
- noteAction.m_column = column;
+ noteAction.m_column = fTickPosition;
noteAction.m_pattern = currentPatternNumber;
noteAction.f_velocity = velocity;
noteAction.f_pan_L = pan_L;
@@ -511,7 +521,7 @@ void Hydrogen::addRealtimeNote( int instrument,
EventQueue::get_instance()->m_addMidiNoteVector.push_back(noteAction);
// hear note if its not in the future
- if ( pPreferences->getHearNewNotes() && position <= getTickPosition() ) {
+ if ( pPreferences->getHearNewNotes() && fTickPosition <= getTickPosition() ) {
hearnote = true;
}
}/* if doRecord */
diff --git a/src/core/LocalFileMgr.cpp b/src/core/LocalFileMgr.cpp
index b30862857..4e30e06de 100644
--- a/src/core/LocalFileMgr.cpp
+++ b/src/core/LocalFileMgr.cpp
@@ -135,6 +135,17 @@ float LocalFileMng::readXmlFloat( QDomNode node , const QString& nodeName, float
}
}
+double LocalFileMng::readXmlDouble( QDomNode node , const QString& nodeName, double defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode)
+{
+ QString text = processNode( node, nodeName, bCanBeEmpty, bShouldExists );
+ if ( text == nullptr ) {
+ _WARNINGLOG( QString( "\tusing default value : '%1' for node '%2'" ).arg( defaultValue ).arg( nodeName ));
+ return defaultValue;
+ } else {
+ return QLocale::c().toDouble( text );
+ }
+}
+
int LocalFileMng::readXmlInt( QDomNode node , const QString& nodeName, int defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode)
{
QString text = processNode( node, nodeName, bCanBeEmpty, bShouldExists );
@@ -600,7 +611,7 @@ int SongWriter::writeSong( Song * pSong, const QString& filename )
assert( pNote );
QDomNode noteNode = doc.createElement( "note" );
- LocalFileMng::writeXmlString( noteNode, "position", QString("%1").arg( pNote->get_position() ) );
+ LocalFileMng::writeXmlString( noteNode, "position", QString("%1").arg( pNote->get_position(), 0, 'g', 10 ) ); //TODO what precision?
LocalFileMng::writeXmlString( noteNode, "leadlag", QString("%1").arg( pNote->get_lead_lag() ) );
LocalFileMng::writeXmlString( noteNode, "velocity", QString("%1").arg( pNote->get_velocity() ) );
LocalFileMng::writeXmlString( noteNode, "pan_L", QString("%1").arg( pNote->get_pan_l() ) );
@@ -610,7 +621,7 @@ int SongWriter::writeSong( Song * pSong, const QString& filename )
LocalFileMng::writeXmlString( noteNode, "key", pNote->key_to_string() );
- LocalFileMng::writeXmlString( noteNode, "length", QString("%1").arg( pNote->get_length() ) );
+ LocalFileMng::writeXmlString( noteNode, "length", QString("%1").arg( pNote->get_length(), 0, 'g', 10 ) ); //TODO what precision?
LocalFileMng::writeXmlString( noteNode, "instrument", QString("%1").arg( pNote->get_instrument()->get_id() ) );
QString noteoff = "false";
diff --git a/src/core/LocalFileMng.h b/src/core/LocalFileMng.h
index aabfa700a..cd1bf9b52 100644
--- a/src/core/LocalFileMng.h
+++ b/src/core/LocalFileMng.h
@@ -61,6 +61,8 @@ class LocalFileMng : public H2Core::Object
static QString readXmlString( QDomNode , const QString& nodeName, const QString& defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false);
static float readXmlFloat( QDomNode , const QString& nodeName, float defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false);
+ static double readXmlDouble( QDomNode node, const QString& nodeName, double defaultValue,
+ bool bCanBeEmpty = false, bool bShouldExists = true, bool tinyXmlCompatMode = false);
static int readXmlInt( QDomNode , const QString& nodeName, int defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false);
static bool readXmlBool( QDomNode , const QString& nodeName, bool defaultValue, bool bShouldExists = true , bool tinyXmlCompatMode = false );
static void convertFromTinyXMLString( QByteArray* str );
diff --git a/src/core/Preferences.cpp b/src/core/Preferences.cpp
index 015664638..74009dd4e 100644
--- a/src/core/Preferences.cpp
+++ b/src/core/Preferences.cpp
@@ -198,7 +198,8 @@ Preferences::Preferences()
mixerFontPointSize = 11;
mixerFalloffSpeed = 1.1;
m_nPatternEditorGridResolution = 8;
- m_bPatternEditorUsingTriplets = false;
+ m_nPatternEditorGridTupletNumerator = 4;
+ m_nPatternEditorGridTupletDenominator = 4;
m_bShowInstrumentPeaks = true;
m_bIsFXTabVisible = true;
m_bShowAutomationArea = false;
@@ -557,9 +558,15 @@ void Preferences::loadPreferences( bool bGlobal )
mixerFalloffSpeed = LocalFileMng::readXmlFloat( guiNode, "mixer_falloff_speed", 1.1f );
// pattern editor grid resolution
- m_nPatternEditorGridResolution = LocalFileMng::readXmlInt( guiNode, "patternEditorGridResolution", m_nPatternEditorGridResolution );
- m_bPatternEditorUsingTriplets = LocalFileMng::readXmlBool( guiNode, "patternEditorUsingTriplets", m_bPatternEditorUsingTriplets );
-
+ m_nPatternEditorGridResolution = LocalFileMng::readXmlInt( guiNode,
+ "patternEditorGridResolution", m_nPatternEditorGridResolution );
+
+ // pattern editor grid Tuplet ratio
+ m_nPatternEditorGridTupletNumerator = LocalFileMng::readXmlInt( guiNode,
+ "patternEditorGridTupletNumerator", m_nPatternEditorGridTupletNumerator );
+ m_nPatternEditorGridTupletDenominator = LocalFileMng::readXmlInt( guiNode,
+ "patternEditorGridTupletDenominator", m_nPatternEditorGridTupletDenominator );
+
m_bShowInstrumentPeaks = LocalFileMng::readXmlBool( guiNode, "showInstrumentPeaks", m_bShowInstrumentPeaks );
m_bIsFXTabVisible = LocalFileMng::readXmlBool( guiNode, "isFXTabVisible", m_bIsFXTabVisible );
m_bShowAutomationArea = LocalFileMng::readXmlBool( guiNode, "showAutomationArea", m_bShowAutomationArea );
@@ -997,10 +1004,14 @@ void Preferences::savePreferences()
LocalFileMng::writeXmlString( guiNode, "mixer_font_family", mixerFontFamily );
LocalFileMng::writeXmlString( guiNode, "mixer_font_pointsize", QString("%1").arg( mixerFontPointSize ) );
LocalFileMng::writeXmlString( guiNode, "mixer_falloff_speed", QString("%1").arg( mixerFalloffSpeed ) );
- LocalFileMng::writeXmlString( guiNode, "patternEditorGridResolution", QString("%1").arg( m_nPatternEditorGridResolution ) );
+ LocalFileMng::writeXmlString( guiNode, "patternEditorGridResolution",
+ QString("%1").arg( m_nPatternEditorGridResolution ) );
+ LocalFileMng::writeXmlString( guiNode, "patternEditorGridTupletNumerator",
+ QString("%1").arg( m_nPatternEditorGridTupletNumerator ) );
+ LocalFileMng::writeXmlString( guiNode, "patternEditorGridTupletDenominator",
+ QString("%1").arg( m_nPatternEditorGridTupletDenominator ) );
LocalFileMng::writeXmlString( guiNode, "patternEditorGridHeight", QString("%1").arg( m_nPatternEditorGridHeight ) );
LocalFileMng::writeXmlString( guiNode, "patternEditorGridWidth", QString("%1").arg( m_nPatternEditorGridWidth ) );
- LocalFileMng::writeXmlBool( guiNode, "patternEditorUsingTriplets", m_bPatternEditorUsingTriplets );
LocalFileMng::writeXmlString( guiNode, "songEditorGridHeight", QString("%1").arg( m_nSongEditorGridHeight ) );
LocalFileMng::writeXmlString( guiNode, "songEditorGridWidth", QString("%1").arg( m_nSongEditorGridWidth ) );
LocalFileMng::writeXmlBool( guiNode, "showInstrumentPeaks", m_bShowInstrumentPeaks );
diff --git a/src/core/Preferences.h b/src/core/Preferences.h
index 176978b81..605728332 100644
--- a/src/core/Preferences.h
+++ b/src/core/Preferences.h
@@ -522,8 +522,13 @@ class Preferences : public H2Core::Object
int getPatternEditorGridResolution();
void setPatternEditorGridResolution( int value );
- bool isPatternEditorUsingTriplets();
- void setPatternEditorUsingTriplets( bool value );
+ int getPatternEditorGridTupletNumerator() const;
+ //void setPatternEditorGridTupletNumerator( int n );
+ int getPatternEditorGridTupletDenominator() const;
+ //void setPatternEditorGridTupletDenominator( int n );
+
+ // setter for both Tuplet numerator and denominator together
+ void setPatternEditorGridTupletRatio( int nTupletNumerator, int nTupletDenominator );
bool isFXTabVisible();
void setFXTabVisible( bool value );
@@ -772,7 +777,8 @@ class Preferences : public H2Core::Object
int mixerFontPointSize;
float mixerFalloffSpeed;
int m_nPatternEditorGridResolution;
- bool m_bPatternEditorUsingTriplets;
+ int m_nPatternEditorGridTupletNumerator;
+ int m_nPatternEditorGridTupletDenominator;
bool m_bShowInstrumentPeaks;
bool m_bIsFXTabVisible;
bool m_bShowAutomationArea;
@@ -1115,11 +1121,26 @@ inline void Preferences::setPatternEditorGridResolution( int value ) {
m_nPatternEditorGridResolution = value;
}
-inline bool Preferences::isPatternEditorUsingTriplets() {
- return m_bPatternEditorUsingTriplets;
+inline int Preferences::getPatternEditorGridTupletNumerator() const {
+ return m_nPatternEditorGridTupletNumerator;
}
-inline void Preferences::setPatternEditorUsingTriplets( bool value ) {
- m_bPatternEditorUsingTriplets = value;
+/*
+inline void Preferences::setPatternEditorGridTupletNumerator( int n ) {
+ m_nPatternEditorGridTupletNumerator = n;
+}*/
+
+
+inline int Preferences::getPatternEditorGridTupletDenominator() const {
+ return m_nPatternEditorGridTupletDenominator;
+}
+/*
+inline void Preferences::setPatternEditorGridTupletDenominator( int n ) {
+ m_nPatternEditorGridTupletDenominator = n;
+}*/
+
+inline void Preferences::setPatternEditorGridTupletRatio( int nTupletNumerator, int nTupletDenominator ) {
+ m_nPatternEditorGridTupletNumerator = nTupletNumerator;
+ m_nPatternEditorGridTupletDenominator = nTupletDenominator;
}
inline bool Preferences::isFXTabVisible() {
diff --git a/src/gui/src/HydrogenApp.cpp b/src/gui/src/HydrogenApp.cpp
index df3b2103e..664e80bb7 100644
--- a/src/gui/src/HydrogenApp.cpp
+++ b/src/gui/src/HydrogenApp.cpp
@@ -660,8 +660,7 @@ void HydrogenApp::onEventQueueTimer()
false,
pQueue->m_addMidiNoteVector[0].b_isMidi,
pQueue->m_addMidiNoteVector[0].b_isInstrumentMode,
- false );
-
+ false ); //TODO 0 4 ?! divbase...
HydrogenApp::get_instance()->m_pUndoStack->push( action );
}
pQueue->m_addMidiNoteVector.erase(pQueue->m_addMidiNoteVector.begin());
diff --git a/src/gui/src/PatternEditor/DrumPatternEditor.cpp b/src/gui/src/PatternEditor/DrumPatternEditor.cpp
index bf5bee59a..e76630108 100644
--- a/src/gui/src/PatternEditor/DrumPatternEditor.cpp
+++ b/src/gui/src/PatternEditor/DrumPatternEditor.cpp
@@ -86,10 +86,10 @@ void DrumPatternEditor::updateEditor( bool bPatternOnly )
updatePatternInfo();
if ( m_pPattern ) {
- m_nEditorWidth = m_nMargin + m_nGridWidth * m_pPattern->get_length();
+ m_nEditorWidth = m_nMargin + m_fGridWidth * m_pPattern->get_length();
}
else {
- m_nEditorWidth = m_nMargin + m_nGridWidth * MAX_NOTES;
+ m_nEditorWidth = m_nMargin + m_fGridWidth * MAX_NOTES;
}
resize( m_nEditorWidth, height() );
@@ -98,43 +98,49 @@ void DrumPatternEditor::updateEditor( bool bPatternOnly )
}
-void DrumPatternEditor::addOrRemoveNote( int nColumn, int nRealColumn, int row,
- bool bDoAdd, bool bDoDelete ) {
+void DrumPatternEditor::addOrRemoveNote( int nGridIndex, int nRealColumn, int row, bool bDoAdd, bool bDoDelete ) {
+//TODO first arg could be easily double fTickPosition
+
+ /* convert gridIndex into the nearest tick */
+ double fTickPosition = nGridIndex * granularity(); // TODO make this a macro?
+
Song *pSong = Hydrogen::get_instance()->getSong();
Instrument *pSelectedInstrument = pSong->getInstrumentList()->get( row );
- H2Core::Note *pOldNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument );
+ // why naming "old"?! old or new depends if the note is already present (delete or add)
int oldLength = -1;
float oldVelocity = 0.8f;
float oldPan_L = 0.5f;
float oldPan_R = 0.5f;
float oldLeadLag = 0.0f;
float fProbability = 1.0f;
+
Note::Key oldNoteKeyVal = Note::C;
Note::Octave oldOctaveKeyVal = Note::P8;
bool isNoteOff = false;
- if ( pOldNote && !bDoDelete ) {
- // Found an old note, but we don't want to delete, so just return.
- return;
+ H2Core::Note *pOldNote = m_pPattern->find_note( fTickPosition, nRealColumn, pSelectedInstrument );
+ if ( pOldNote ) {
+ // Found an old note matching the same position
+ if ( !bDoDelete ) { // we don't want to delete, so just return.
+ return;
+ } else { // note will be deleted, so here "old" has sense
+ oldLength = pOldNote->get_length();
+ oldVelocity = pOldNote->get_velocity();
+ oldPan_L = pOldNote->get_pan_l();
+ oldPan_R = pOldNote->get_pan_r();
+ oldLeadLag = pOldNote->get_lead_lag();
+ oldNoteKeyVal = pOldNote->get_key();
+ oldOctaveKeyVal = pOldNote->get_octave();
+ isNoteOff = pOldNote->get_note_off(); // not "old" here?! ;)
+ fProbability = pOldNote->get_probability();
+ }
} else if ( !pOldNote && !bDoAdd ) {
// No note there, but we don't want to add a new one, so return.
return;
}
- if ( pOldNote ) {
- oldLength = pOldNote->get_length();
- oldVelocity = pOldNote->get_velocity();
- oldPan_L = pOldNote->get_pan_l();
- oldPan_R = pOldNote->get_pan_r();
- oldLeadLag = pOldNote->get_lead_lag();
- oldNoteKeyVal = pOldNote->get_key();
- oldOctaveKeyVal = pOldNote->get_octave();
- isNoteOff = pOldNote->get_note_off();
- fProbability = pOldNote->get_probability();
- }
-
- SE_addOrDeleteNoteAction *action = new SE_addOrDeleteNoteAction( nColumn,
+ SE_addOrDeleteNoteAction *action = new SE_addOrDeleteNoteAction( fTickPosition,
row,
m_nSelectedPatternNumber,
oldLength,
@@ -169,14 +175,14 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
if (row >= nInstruments) {
return;
}
- int nColumn = getColumn( ev->x(), /* bUseFineGrained=*/ true );
- int nRealColumn = 0;
+ double fTickPosition = getColumn( ev->x(), /* bUseFineGrained=*/ true ); // position of nearest grid mark in ticks
+ int nGridIndex = getGridIndex( ev->x() ); // index of nearest grid mark //TODO could be avoided
+ int nRealColumn = 0; // TODO what is the use of this? does it affect tuplets? currently it is not rounded
if( ev->x() > m_nMargin ) {
- nRealColumn = ( ev->x() - m_nMargin) / static_cast(m_nGridWidth);
+ nRealColumn = ( ev->x() - m_nMargin ) / m_fGridWidth;
}
-
- if ( nColumn >= (int)m_pPattern->get_length() ) {
+ if ( fTickPosition >= m_pPattern->get_length() ) { // here was a (int) cast
update( 0, 0, width(), height() );
return;
}
@@ -186,9 +192,9 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
{
//shift + leftClick: add noteOff note
HydrogenApp *pApp = HydrogenApp::get_instance();
- Note *pNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
+ Note *pNote = m_pPattern->find_note( fTickPosition, nRealColumn, pSelectedInstrument, false );
if ( pNote != nullptr ) {
- SE_addOrDeleteNoteAction *action = new SE_addOrDeleteNoteAction( nColumn,
+ SE_addOrDeleteNoteAction *action = new SE_addOrDeleteNoteAction( fTickPosition,
row,
m_nSelectedPatternNumber,
pNote->get_length(),
@@ -207,7 +213,7 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
pApp->m_pUndoStack->push( action );
} else {
// Add stop-note
- SE_addNoteOffAction *action = new SE_addNoteOffAction( nColumn, row, m_nSelectedPatternNumber,
+ SE_addNoteOffAction *action = new SE_addNoteOffAction( fTickPosition, row, m_nSelectedPatternNumber,
pNote != nullptr );
pApp->m_pUndoStack->push( action );
}
@@ -215,7 +221,7 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
else if ( ev->button() == Qt::LeftButton ) {
pHydrogen->setSelectedInstrumentNumber( row );
- addOrRemoveNote( nColumn, nRealColumn, row );
+ addOrRemoveNote( nGridIndex, nRealColumn, row ); //TODO first arg could be easily double fTickPosition
m_selection.clearSelection();
} else if ( ev->button() == Qt::RightButton ) {
@@ -228,7 +234,7 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
pHydrogen->setSelectedInstrumentNumber( row );
}
- m_pPatternEditorPanel->setCursorPosition( nColumn );
+ m_pPatternEditorPanel->setCursorIndexPosition( nGridIndex );
HydrogenApp::get_instance()->setHideKeyboardCursor( true );
update();
}
@@ -238,20 +244,20 @@ void DrumPatternEditor::mouseDragStartEvent( QMouseEvent *ev )
int row = (int)( ev->y() / (float)m_nGridHeight);
Hydrogen *pHydrogen = Hydrogen::get_instance();
Song *pSong = pHydrogen->getSong();
- int nColumn = getColumn( ev->x() );
+ double fTickPosition = getColumn( ev->x() );
if ( ev->button() == Qt::RightButton ) {
// Right button drag: adjust note length
int nRealColumn = 0;
Instrument *pSelectedInstrument = pSong->getInstrumentList()->get( row );
if( ev->x() > m_nMargin ) {
- nRealColumn = ( ev->x() - m_nMargin) / static_cast(m_nGridWidth);
+ nRealColumn = ( ev->x() - m_nMargin) / m_fGridWidth;
}
- m_pDraggedNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
+ m_pDraggedNote = m_pPattern->find_note( fTickPosition, nRealColumn, pSelectedInstrument, false );
// needed for undo note length
__nRealColumn = nRealColumn;
- __nColumn = nColumn;
+ m_fTickPosition = fTickPosition;
__row = row;
if( m_pDraggedNote ){
__oldLength = m_pDraggedNote->get_length();
@@ -260,13 +266,14 @@ void DrumPatternEditor::mouseDragStartEvent( QMouseEvent *ev )
}
} else {
// Other drag (selection or move) we'll set the cursor input position to the start of the gesture
- pHydrogen->setSelectedInstrumentNumber( row );
- m_pPatternEditorPanel->setCursorPosition( nColumn );
+ pHydrogen->setSelectedInstrumentNumber( row );
+ int nGridIndex = getGridIndex( ev->x() ); // position in grid marks
+ m_pPatternEditorPanel->setCursorIndexPosition( nGridIndex );
HydrogenApp::get_instance()->setHideKeyboardCursor( true );
}
}
-void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
+void DrumPatternEditor::addOrDeleteNoteAction( double fTickPosition,
int row,
int selectedPatternNumber,
int oldLength,
@@ -303,9 +310,9 @@ void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
if ( isDelete ) {
// Find and delete an existing (matching) note.
- Pattern::notes_t *notes = (Pattern::notes_t *)pPattern->get_notes();
+ Pattern::notes_t *notes = (Pattern::notes_t *) pPattern->get_notes();
bool bFound = false;
- FOREACH_NOTE_IT_BOUND( notes, it, nColumn ) {
+ FOREACH_NOTE_IT_BOUND( notes, it, fTickPosition ) {
Note *pNote = it->second;
assert( pNote );
if ( ( isNoteOff && pNote->get_note_off() )
@@ -313,7 +320,9 @@ void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
&& pNote->get_key() == oldNoteKeyVal
&& pNote->get_octave() == oldOctaveKeyVal
&& pNote->get_velocity() == oldVelocity
- && pNote->get_probability() == fProbability ) ) {
+ && pNote->get_probability() == fProbability
+ )
+ ) {
delete pNote;
notes->erase( it );
bFound = true;
@@ -326,7 +335,6 @@ void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
} else {
// create the new note
- unsigned nPosition = nColumn;
float fVelocity = oldVelocity;
float fPan_L = oldPan_L ;
float fPan_R = oldPan_R;
@@ -344,7 +352,7 @@ void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
float fPitch = 0.f;
- Note *pNote = new Note( pSelectedInstrument, nPosition, fVelocity, fPan_L, fPan_R, nLength, fPitch );
+ Note *pNote = new Note( pSelectedInstrument, fTickPosition, fVelocity, fPan_L, fPan_R, nLength, fPitch );
pNote->set_note_off( isNoteOff );
if ( !isNoteOff ) {
pNote->set_lead_lag( oldLeadLag );
@@ -374,14 +382,15 @@ void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
}
-// Find a note that matches pNote, and move it from (nColumn, nRow) to (nNewColumn, nNewRow)
-void DrumPatternEditor::moveNoteAction( int nColumn,
+// Find a note that matches pNote, and move it from (fColumn, nRow) to (fNewColumn, nNewRow)
+void DrumPatternEditor::moveNoteAction( double fColumn,
int nRow,
int nPattern,
- int nNewColumn,
+ double fNewColumn,
int nNewRow,
Note *pNote)
{
+
Hydrogen *pHydrogen = Hydrogen::get_instance();
Song *pSong = pHydrogen->getSong();
@@ -400,7 +409,7 @@ void DrumPatternEditor::moveNoteAction( int nColumn,
Instrument *pFromInstrument = pInstrumentList->get( nRow ),
*pToInstrument = pInstrumentList->get( nNewRow );
- FOREACH_NOTE_IT_BOUND((Pattern::notes_t *)pPattern->get_notes(), it, nColumn) {
+ FOREACH_NOTE_IT_BOUND((Pattern::notes_t *)pPattern->get_notes(), it, fColumn) {
Note *pCandidateNote = it->second;
if ( pCandidateNote->get_instrument() == pFromInstrument
&& pCandidateNote->get_key() == pNote->get_key()
@@ -424,9 +433,10 @@ void DrumPatternEditor::moveNoteAction( int nColumn,
}
pPattern->remove_note( pFoundNote );
+
if ( pFromInstrument == pToInstrument ) {
// Note can simply be moved.
- pFoundNote->set_position( nNewColumn );
+ pFoundNote->set_position( fNewColumn );
pPattern->insert_note( pFoundNote );
} else {
pPattern->remove_note( pFoundNote );
@@ -436,7 +446,7 @@ void DrumPatternEditor::moveNoteAction( int nColumn,
m_selection.removeFromSelection( pFoundNote, /* bCheck=*/false );
m_selection.addToSelection( pNewNote );
}
- pNewNote->set_position( nNewColumn );
+ pNewNote->set_position( fNewColumn );
m_selection.addToSelection( pNewNote );
pPattern->insert_note( pNewNote );
delete pFoundNote;
@@ -477,7 +487,7 @@ void DrumPatternEditor::mouseDragEndEvent( QMouseEvent *ev )
void DrumPatternEditor::selectionMoveEndEvent( QInputEvent *ev )
{
updateModifiers( ev );
- QPoint offset = movingGridOffset();
+ QPointF offset = movingGridOffset();
if ( offset.x() == 0 && offset.y() == 0 ) {
// Move with no effect.
return;
@@ -507,17 +517,17 @@ void DrumPatternEditor::selectionMoveEndEvent( QInputEvent *ev )
for ( auto pNote : selectedNotes ) {
int nInstrument = pInstrumentList->index( pNote->get_instrument() );
- int nPosition = pNote->get_position();
+ double fPosition = pNote->get_position();
int nNewInstrument = nInstrument + offset.y();
- int nNewPosition = nPosition + offset.x();
+ double fNewPosition = fPosition + offset.x();
if ( nNewInstrument < 0 || nNewInstrument >= pInstrumentList->size()
- || nNewPosition < 0 || nNewPosition >= m_pPattern->get_length() ) {
+ || fNewPosition < 0 || fNewPosition >= m_pPattern->get_length() ) {
if ( m_bCopyNotMove ) {
// Copying a note to an out-of-range location. Nothing to do.
} else {
// Note is moved out of range. Delete it.
- pUndo->push( new SE_addOrDeleteNoteAction( nPosition,
+ pUndo->push( new SE_addOrDeleteNoteAction( fPosition,
nInstrument,
m_nSelectedPatternNumber,
pNote->get_length(),
@@ -538,7 +548,7 @@ void DrumPatternEditor::selectionMoveEndEvent( QInputEvent *ev )
} else {
if ( m_bCopyNotMove ) {
// Copy note to a new note.
- pUndo->push( new SE_addOrDeleteNoteAction( nNewPosition,
+ pUndo->push( new SE_addOrDeleteNoteAction( fNewPosition,
nNewInstrument,
m_nSelectedPatternNumber,
pNote->get_length(),
@@ -556,8 +566,8 @@ void DrumPatternEditor::selectionMoveEndEvent( QInputEvent *ev )
false ) );
} else {
// Move note
- pUndo->push( new SE_moveNoteAction( nPosition, nInstrument, m_nSelectedPatternNumber,
- nNewPosition, nNewInstrument, pNote ) );
+ pUndo->push( new SE_moveNoteAction( fPosition, nInstrument, m_nSelectedPatternNumber,
+ fNewPosition, nNewInstrument, pNote ) );
}
}
}
@@ -566,7 +576,7 @@ void DrumPatternEditor::selectionMoveEndEvent( QInputEvent *ev )
}
-void DrumPatternEditor::editNoteLengthAction( int nColumn, int nRealColumn, int row, int length, int selectedPatternNumber )
+void DrumPatternEditor::editNoteLengthAction( double fColumn, int nRealColumn, int row, double length, int selectedPatternNumber )
{
Hydrogen *pHydrogen = Hydrogen::get_instance();
PatternList *pPatternList = pHydrogen->getSong()->getPatternList();
@@ -583,7 +593,7 @@ void DrumPatternEditor::editNoteLengthAction( int nColumn, int nRealColumn, int
m_pAudioEngine->lock( RIGHT_HERE );
- pDraggedNote = pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
+ pDraggedNote = pPattern->find_note( fColumn, nRealColumn, pSelectedInstrument, false );
if( pDraggedNote ){
pDraggedNote->set_length( length );
}
@@ -668,7 +678,7 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
} else if ( ev->matches( QKeySequence::MoveToEndOfLine ) || ev->matches( QKeySequence::SelectEndOfLine ) ) {
// -->|
- m_pPatternEditorPanel->setCursorPosition( m_pPattern->get_length() );
+ m_pPatternEditorPanel->setCursorIndexPosition( (int) m_pPattern->get_length() / granularity() );
} else if ( ev->matches( QKeySequence::MoveToPreviousChar ) || ev->matches( QKeySequence::SelectPreviousChar ) ) {
// <-
@@ -680,7 +690,7 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
} else if ( ev->matches( QKeySequence::MoveToStartOfLine ) || ev->matches( QKeySequence::SelectStartOfLine ) ) {
// |<--
- m_pPatternEditorPanel->setCursorPosition( 0 );
+ m_pPatternEditorPanel->setCursorIndexPosition( 0 );
} else if ( ev->matches( QKeySequence::MoveToNextLine ) || ev->matches( QKeySequence::SelectNextLine ) ) {
if ( nSelectedInstrument + 1 < nMaxInstrument ) {
@@ -726,7 +736,7 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
} else if ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) {
// Key: Enter / Return: add or remove note at current position
m_selection.clearSelection();
- addOrRemoveNote( m_pPatternEditorPanel->getCursorPosition(), -1, nSelectedInstrument );
+ addOrRemoveNote( m_pPatternEditorPanel->getCursorIndexPosition(), -1, nSelectedInstrument );
} else if ( ev->key() == Qt::Key_Delete ) {
// Key: Delete / Backspace: delete selected notes, or note under keyboard cursor
@@ -736,7 +746,7 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
deleteSelection();
} else {
// Delete note under the keyboard cursor.
- addOrRemoveNote( m_pPatternEditorPanel->getCursorPosition(), -1, nSelectedInstrument,
+ addOrRemoveNote( m_pPatternEditorPanel->getCursorIndexPosition(), -1, nSelectedInstrument,
/*bDoAdd=*/false, /*bDoDelete=*/true);
}
@@ -804,8 +814,8 @@ std::vector DrumPatternEditor::elementsInters
// Calculate the first and last position values that this rect will intersect with
- int x_min = (r.left() - m_nMargin - 1) / m_nGridWidth;
- int x_max = (r.right() - m_nMargin) / m_nGridWidth;
+ int x_min = (r.left() - m_nMargin - 1) / m_fGridWidth;
+ int x_max = (r.right() - m_nMargin) / m_fGridWidth;
const Pattern::notes_t* notes = m_pPattern->get_notes();
std::vector result;
@@ -813,7 +823,7 @@ std::vector DrumPatternEditor::elementsInters
for (auto it = notes->lower_bound( x_min ); it != notes->end() && it->first <= x_max; ++it ) {
Note *note = it->second;
int nInstrument = pInstrList->index( note->get_instrument() );
- uint x_pos = m_nMargin + (it->first * m_nGridWidth);
+ uint x_pos = m_nMargin + (it->first * m_fGridWidth);
uint y_pos = ( nInstrument * m_nGridHeight) + (m_nGridHeight / 2) - 3;
if ( r.contains( QPoint( x_pos, y_pos + h/2) ) ) {
@@ -829,12 +839,10 @@ std::vector DrumPatternEditor::elementsInters
///
QRect DrumPatternEditor::getKeyboardCursorRect()
{
-
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_nGridWidth;
+ uint x = round( m_nMargin + m_pPatternEditorPanel->getCursorIndexPosition()* granularity() * m_fGridWidth ); // TODO check
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
uint y = nSelectedInstrument * m_nGridHeight;
- return QRect( x-m_nGridWidth*3, y+2, m_nGridWidth*6, m_nGridHeight-3 );
-
+ return QRect( x-m_fGridWidth*3, y+2, m_fGridWidth*6, m_nGridHeight-3 );
}
void DrumPatternEditor::selectAll()
@@ -901,7 +909,8 @@ void DrumPatternEditor::paste()
QUndoStack *pUndo = HydrogenApp::get_instance()->m_pUndoStack;
InstrumentList *pInstrList = Hydrogen::get_instance()->getSong()->getInstrumentList();
XMLNode noteList;
- int nDeltaPos = 0, nDeltaInstrument = 0;
+ double fDeltaPos = 0.;
+ int nDeltaInstrument = 0;
XMLDoc doc;
if ( ! doc.setContent( clipboard->text() ) ) {
@@ -926,10 +935,10 @@ void DrumPatternEditor::paste()
// it to adjust the location relative to the current keyboard
// input cursor.
if ( !positionNode.isNull() ) {
- int nCurrentPos = m_pPatternEditorPanel->getCursorPosition();
+ double fCurrentPos = m_pPatternEditorPanel->getCursorIndexPosition() * granularity();
int nCurrentInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
- nDeltaPos = nCurrentPos - positionNode.read_int( "position", nCurrentPos );
+ fDeltaPos = fCurrentPos - positionNode.read_double( "position", fCurrentPos );
nDeltaInstrument = nCurrentInstrument - positionNode.read_int( "instrument", nCurrentInstrument );
}
@@ -970,12 +979,12 @@ void DrumPatternEditor::paste()
pUndo->beginMacro( "paste notes" );
for ( XMLNode n = noteList.firstChildElement( "note" ); ! n.isNull(); n = n.nextSiblingElement() ) {
Note *pNote = Note::load_from( &n, pInstrList );
- int nPos = pNote->get_position() + nDeltaPos;
+ double fPos = pNote->get_position() + fDeltaPos;
int nInstrument = pInstrList->index( pNote->get_instrument() ) + nDeltaInstrument;
- if ( nPos >= 0 && nPos < m_pPattern->get_length()
+ if ( fPos >= 0 && fPos < m_pPattern->get_length()
&& nInstrument >= 0 && nInstrument < pInstrList->size() ) {
- pUndo->push( new SE_addOrDeleteNoteAction( nPos,
+ pUndo->push( new SE_addOrDeleteNoteAction( fPos,
nInstrument,
m_nSelectedPatternNumber,
pNote->get_length(),
@@ -1032,7 +1041,7 @@ void DrumPatternEditor::__draw_pattern(QPainter& painter)
for ( uint nInstr = 0; nInstr < pInstrList->size(); ++nInstr ) {
uint y = m_nGridHeight * nInstr;
if ( nInstr == (uint)nSelectedInstrument ) { // selected instrument
- painter.fillRect( 0, y + 1, ( m_nMargin + nNotes * m_nGridWidth ), m_nGridHeight - 1, selectedRowColor );
+ painter.fillRect( 0, y + 1, ( m_nMargin + nNotes * m_fGridWidth ), m_nGridHeight - 1, selectedRowColor );
}
}
@@ -1043,14 +1052,14 @@ void DrumPatternEditor::__draw_pattern(QPainter& painter)
// Draw cursor
if ( hasFocus() && !HydrogenApp::get_instance()->hideKeyboardCursor() ) {
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_nGridWidth;
+ uint x = round( m_nMargin + m_pPatternEditorPanel->getCursorIndexPosition()* granularity() * m_fGridWidth );
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
uint y = nSelectedInstrument * m_nGridHeight;
QPen p( Qt::black );
p.setWidth( 2 );
painter.setPen( p );
painter.setRenderHint( QPainter::Antialiasing );
- painter.drawRoundedRect( QRect( x-m_nGridWidth*3, y+2, m_nGridWidth*6, m_nGridHeight-3 ), 4, 4 );
+ painter.drawRoundedRect( QRect( x-m_fGridWidth*3, y+2, m_fGridWidth*6, m_nGridHeight-3 ), 4, 4 );
}
@@ -1078,11 +1087,11 @@ void DrumPatternEditor::__draw_pattern(QPainter& painter)
// markers for instruments which have more than one note in the same position (a chord or genuine
// duplicates)
for ( auto posIt = pNotes->begin(); posIt != pNotes->end(); ) {
- int nPosition = posIt->second->get_position();
+ double fPosition = posIt->second->get_position();
// Process all notes at this position
auto noteIt = posIt;
- while ( noteIt != pNotes->end() && noteIt->second->get_position() == nPosition ) {
+ while ( noteIt != pNotes->end() && noteIt->second->get_position() == fPosition ) {
Note *pNote = noteIt->second;
int nInstrumentID = pNote->get_instrument_id();
@@ -1106,7 +1115,7 @@ void DrumPatternEditor::__draw_pattern(QPainter& painter)
if ( noteCount[ nInstrumentID ] > 1 ) {
// Draw "2x" text to the left of the note
int nInstrument = pInstrList->index( pInstrument );
- int x = m_nMargin + (nPosition * m_nGridWidth);
+ int x = m_nMargin + round( fPosition * m_fGridWidth );
int y = ( nInstrument * m_nGridHeight);
const int boxWidth = 128;
QFont font;
@@ -1140,8 +1149,7 @@ void DrumPatternEditor::__draw_note( Note *note, QPainter& p )
ERRORLOG( "Instrument not found..skipping note" );
return;
}
-
- QPoint pos ( m_nMargin + note->get_position() * m_nGridWidth,
+ QPoint pos ( m_nMargin + round( note->get_position() * m_fGridWidth ),
( nInstrument * m_nGridHeight) + (m_nGridHeight / 2) - 3 );
drawNoteSymbol( p, pos, note );
@@ -1170,13 +1178,12 @@ void DrumPatternEditor::__draw_grid( QPainter& p )
for ( uint i = 0; i < (uint)nInstruments; i++ ) {
uint y = m_nGridHeight * i + 1;
if ( i == (uint)nSelectedInstrument ) {
- p.fillRect( 0, y, (m_nMargin + nNotes * m_nGridWidth), (int)( m_nGridHeight * 0.7 ), selectedRowColor );
+ p.fillRect( 0, y, (m_nMargin + nNotes * m_fGridWidth), (int)( m_nGridHeight * 0.7 ), selectedRowColor );
}
else {
- p.fillRect( 0, y, (m_nMargin + nNotes * m_nGridWidth), (int)( m_nGridHeight * 0.7 ), backgroundColor );
+ p.fillRect( 0, y, (m_nMargin + nNotes * m_fGridWidth), (int)( m_nGridHeight * 0.7 ), backgroundColor );
}
}
-
}
@@ -1201,11 +1208,11 @@ void DrumPatternEditor::__create_background( QPainter& p)
resize( width(), m_nEditorHeight );
}
- p.fillRect(0, 0, m_nMargin + nNotes * m_nGridWidth, height(), backgroundColor);
+ p.fillRect(0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor);
for ( uint i = 0; i < (uint)nInstruments; i++ ) {
uint y = m_nGridHeight * i;
if ( ( i % 2) != 0) {
- p.fillRect( 0, y, (m_nMargin + nNotes * m_nGridWidth), m_nGridHeight, alternateRowColor );
+ p.fillRect( 0, y, (m_nMargin + nNotes * m_fGridWidth), m_nGridHeight, alternateRowColor );
}
}
@@ -1213,10 +1220,10 @@ void DrumPatternEditor::__create_background( QPainter& p)
p.setPen( lineColor );
for ( uint i = 0; i < (uint)nInstruments; i++ ) {
uint y = m_nGridHeight * i + m_nGridHeight;
- p.drawLine( 0, y, (m_nMargin + nNotes * m_nGridWidth), y);
+ p.drawLine( 0, y, (m_nMargin + nNotes * m_fGridWidth), y);
}
- p.drawLine( 0, m_nEditorHeight, (m_nMargin + nNotes * m_nGridWidth), m_nEditorHeight );
+ p.drawLine( 0, m_nEditorHeight, (m_nMargin + nNotes * m_fGridWidth), m_nEditorHeight );
}
@@ -1287,7 +1294,7 @@ void DrumPatternEditor::selectedPatternChangedEvent()
///NotePropertiesRuler undo redo action
-void DrumPatternEditor::undoRedoAction( int column,
+void DrumPatternEditor::undoRedoAction( double column,
QString mode,
int nSelectedPatternNumber,
int nSelectedInstrument,
@@ -1313,7 +1320,7 @@ void DrumPatternEditor::undoRedoAction( int column,
FOREACH_NOTE_CST_IT_BOUND(notes,it,column) {
Note *pNote = it->second;
assert( pNote );
- assert( (int)pNote->get_position() == column );
+ //assert( (int)pNote->get_position() == column ); // TODO tolerance. is this redundant?
if ( pNote->get_instrument() != pSong->getInstrumentList()->get( nSelectedInstrument ) ) {
continue;
}
@@ -1502,7 +1509,9 @@ void DrumPatternEditor::functionPasteNotesRedoAction(std::list
-void DrumPatternEditor::functionFillNotesUndoAction( QStringList noteList, int nSelectedInstrument, int patternNumber )
+//void DrumPatternEditor::functionFillNotesUndoAction( QStringList noteList, int nSelectedInstrument, int patternNumber )
+void DrumPatternEditor::functionFillNotesUndoAction( std::vector notePositions,
+ int nSelectedInstrument, int patternNumber )
{
Hydrogen * H = Hydrogen::get_instance();
PatternList *pPatternList = Hydrogen::get_instance()->getSong()->getPatternList();
@@ -1511,10 +1520,9 @@ void DrumPatternEditor::functionFillNotesUndoAction( QStringList noteList, int n
m_pAudioEngine->lock( RIGHT_HERE ); // lock the audio engine
- for (int i = 0; i < noteList.size(); i++ ) {
- int nColumn = noteList.value(i).toInt();
+ for ( int i = 0; i < notePositions.size(); i++ ) {
Pattern::notes_t* notes = (Pattern::notes_t*)pPattern->get_notes();
- FOREACH_NOTE_IT_BOUND(notes,it,nColumn) {
+ FOREACH_NOTE_IT_BOUND( notes, it, notePositions[i] ) {
Note *pNote = it->second;
assert( pNote );
if ( pNote->get_instrument() == pSelectedInstrument ) {
@@ -1532,7 +1540,10 @@ void DrumPatternEditor::functionFillNotesUndoAction( QStringList noteList, int n
}
-void DrumPatternEditor::functionFillNotesRedoAction( QStringList noteList, int nSelectedInstrument, int patternNumber )
+//void DrumPatternEditor::functionFillNotesRedoAction( QStringList noteList, int nSelectedInstrument, int patternNumber )
+
+void DrumPatternEditor::functionFillNotesRedoAction( std::vector notePositions,
+ int nSelectedInstrument, int patternNumber )
{
Hydrogen * H = Hydrogen::get_instance();
PatternList *pPatternList = Hydrogen::get_instance()->getSong()->getPatternList();
@@ -1546,11 +1557,10 @@ void DrumPatternEditor::functionFillNotesRedoAction( QStringList noteList, int n
const int nLength = -1;
m_pAudioEngine->lock( RIGHT_HERE ); // lock the audio engine
- for (int i = 0; i < noteList.size(); i++ ) {
+ for (int i = 0; i < notePositions.size(); i++ ) {
// create the new note
- int position = noteList.value(i).toInt();
- Note *pNote = new Note( pSelectedInstrument, position, velocity, pan_L, pan_R, nLength, fPitch );
+ Note *pNote = new Note( pSelectedInstrument, notePositions[i], velocity, pan_L, pan_R, nLength, fPitch );
pPattern->insert_note( pNote );
}
m_pAudioEngine->unlock(); // unlock the audio engine
@@ -1561,7 +1571,7 @@ void DrumPatternEditor::functionFillNotesRedoAction( QStringList noteList, int n
void DrumPatternEditor::functionRandomVelocityAction( QStringList noteVeloValue, int nSelectedInstrument, int selectedPatternNumber )
-{
+{ // TODO why QStringList and not std::vector ? Does it save space?
Hydrogen * H = Hydrogen::get_instance();
PatternList *pPatternList = Hydrogen::get_instance()->getSong()->getPatternList();
Pattern *pPattern = pPatternList->get( selectedPatternNumber );
@@ -1570,18 +1580,19 @@ void DrumPatternEditor::functionRandomVelocityAction( QStringList noteVeloValue,
m_pAudioEngine->lock( RIGHT_HERE ); // lock the audio engine
- int nResolution = granularity();
+ double fResolution = granularity();
int positionCount = 0;
- for (int i = 0; i < pPattern->get_length(); i += nResolution) {
+ for (int i = 0; i*fResolution < pPattern->get_length(); i++ ) { //TODO while() loop using variable float fPosition
const Pattern::notes_t* notes = pPattern->get_notes();
- FOREACH_NOTE_CST_IT_BOUND(notes,it,i) {
+ FOREACH_NOTE_CST_IT_BOUND(notes,it,i*fResolution) {
Note *pNote = it->second;
if ( pNote->get_instrument() == pSelectedInstrument) {
- float velocity = noteVeloValue.value( positionCount ).toFloat();
+ float velocity = noteVeloValue.value( positionCount ).toFloat(); // TODO why QStringList and not std::vector ?
pNote->set_velocity(velocity);
positionCount++;
}
}
+
}
H->getSong()->setIsModified( true );
m_pAudioEngine->unlock(); // unlock the audio engine
diff --git a/src/gui/src/PatternEditor/DrumPatternEditor.h b/src/gui/src/PatternEditor/DrumPatternEditor.h
index 48a796796..68330085e 100644
--- a/src/gui/src/PatternEditor/DrumPatternEditor.h
+++ b/src/gui/src/PatternEditor/DrumPatternEditor.h
@@ -33,6 +33,7 @@
#include
#include
+#include
class PatternEditorInstrumentList;
@@ -54,7 +55,7 @@ class DrumPatternEditor : public PatternEditor
virtual void selectedPatternChangedEvent() override;
virtual void selectedInstrumentChangedEvent() override;
//~ Implements EventListener interface
- void addOrDeleteNoteAction( int nColumn,
+ void addOrDeleteNoteAction( double fTickPosition,
int row,
int selectedPatternNumber,
int oldLength,
@@ -70,16 +71,16 @@ class DrumPatternEditor : public PatternEditor
bool isInstrumentMode,
bool isNoteOff,
bool isDelete );
- void moveNoteAction( int nColumn,
+ void moveNoteAction( double fColumn,
int nRow,
int nPattern,
- int nNewColumn,
+ double fNewColumn,
int nNewRow,
H2Core::Note *note);
- void addOrRemoveNote( int nColumn, int nRealColumn, int row, bool bDoAdd = true, bool bDoDelete = true );
- void editNoteLengthAction( int nColumn, int nRealColumn, int row, int length, int selectedPatternNumber );
- void undoRedoAction( int column,
+ void addOrRemoveNote( int nGridIndex, int nRealColumn, int row, bool bDoAdd = true, bool bDoDelete = true ); //TODO first arg could be easily double fTickPosition
+ void editNoteLengthAction( double fColumn, int nRealColumn, int row, double length, int selectedPatternNumber );
+ void undoRedoAction( double column,
QString mode,
int nSelectedPatternNumber,
int nSelectedInstrument,
@@ -92,8 +93,10 @@ class DrumPatternEditor : public PatternEditor
int octaveKeyVal );
void functionClearNotesRedoAction( int nSelectedInstrument, int selectedPatternNumber );
void functionClearNotesUndoAction( std::list< H2Core::Note* > noteList, int nSelectedInstrument, int patternNumber );
- void functionFillNotesUndoAction( QStringList noteList, int nSelectedInstrument, int patternNumber );
- void functionFillNotesRedoAction( QStringList noteList, int nSelectedInstrument, int patternNumber );
+ //void functionFillNotesUndoAction( QStringList noteList, int nSelectedInstrument, int patternNumber );
+ void functionFillNotesUndoAction( std::vector notePositions, int nSelectedInstrument, int patternNumber );
+ //void functionFillNotesRedoAction( QStringList noteList, int nSelectedInstrument, int patternNumber );
+ void functionFillNotesRedoAction( std::vector notePositions, int nSelectedInstrument, int patternNumber );
void functionRandomVelocityAction( QStringList noteVeloValue, int nSelectedInstrument, int selectedPatternNumber );
void functionMoveInstrumentAction( int nSourceInstrument, int nTargetInstrument );
void functionDropInstrumentUndoAction( int nTargetInstrument, std::vector* AddedComponents );
@@ -148,9 +151,9 @@ class DrumPatternEditor : public PatternEditor
QString renameCompo( QString OriginalName );
int __nRealColumn;
- int __nColumn;
+ double m_fTickPosition;
int __row;
- int __oldLength;
+ double __oldLength;
};
diff --git a/src/gui/src/PatternEditor/NotePropertiesRuler.cpp b/src/gui/src/PatternEditor/NotePropertiesRuler.cpp
index 13fe00d7a..ff573f70c 100644
--- a/src/gui/src/PatternEditor/NotePropertiesRuler.cpp
+++ b/src/gui/src/PatternEditor/NotePropertiesRuler.cpp
@@ -49,8 +49,8 @@ NotePropertiesRuler::NotePropertiesRuler( QWidget *parent, PatternEditorPanel *p
m_Mode = mode;
- m_nGridWidth = (Preferences::get_instance())->getPatternEditorGridWidth();
- m_nEditorWidth = m_nMargin + m_nGridWidth * ( MAX_NOTES * 4 );
+ m_fGridWidth = (Preferences::get_instance())->getPatternEditorGridWidth();
+ m_nEditorWidth = m_nMargin + m_fGridWidth * ( MAX_NOTES * 4 );
m_fLastSetValue = 0.0;
m_bValueHasBeenSet = false;
@@ -126,9 +126,10 @@ void NotePropertiesRuler::wheelEvent(QWheelEvent *ev )
fDelta = fDelta * -1.0;
}
- int nColumn = getColumn( ev->x() );
+ double m_fTickPosition = getColumn( ev->x() );
+ int nGridIndex = getGridIndex( ev->x() ); //unused
- m_pPatternEditorPanel->setCursorPosition( nColumn );
+ m_pPatternEditorPanel->setCursorIndexPosition( nGridIndex );
HydrogenApp::get_instance()->setHideKeyboardCursor( true );
Song *pSong = pHydrogen->getSong();
@@ -141,7 +142,7 @@ void NotePropertiesRuler::wheelEvent(QWheelEvent *ev )
notes.push_back( pNote );
}
} else {
- FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, nColumn ) {
+ FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, m_fTickPosition ) {
notes.push_back( it->second );
}
}
@@ -267,9 +268,9 @@ void NotePropertiesRuler::selectionMoveCancelEvent() {
void NotePropertiesRuler::mouseMoveEvent( QMouseEvent *ev )
{
if ( ev->buttons() == Qt::NoButton ) {
- int nColumn = getColumn( ev->x() );
+ double fTickPosition = getColumn( ev->x() );
bool bFound = false;
- FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, nColumn ) {
+ FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, fTickPosition ) {
bFound = true;
break;
}
@@ -317,8 +318,8 @@ void NotePropertiesRuler::prepareUndoAction( int x )
} else {
// No notes are selected. The target notes to adjust are all those at column given by 'x', so we preserve these.
- int nColumn = getColumn( x );
- FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, nColumn ) {
+ double m_fTickPosition = getColumn( x );
+ FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, m_fTickPosition ) {
Note *pNote = it->second;
if ( pNote->get_instrument() == pSelectedInstrument ) {
m_oldNotes[ pNote ] = new Note( pNote );
@@ -336,12 +337,13 @@ void NotePropertiesRuler::propertyDragUpdate( QMouseEvent *ev )
return;
}
- int nColumn = getColumn( ev->x() );
+ double m_fTickPosition = getColumn( ev->x() );
+ int nGridIndex = getGridIndex( ev->x() );
- m_pPatternEditorPanel->setCursorPosition( nColumn );
+ m_pPatternEditorPanel->setCursorIndexPosition( nGridIndex );
HydrogenApp::get_instance()->setHideKeyboardCursor( true );
- if ( m_nDragPreviousColumn != nColumn ) {
+ if ( m_fDragPreviousColumn != m_fTickPosition ) {
// Complete current undo action, and start a new one.
addUndoAction();
prepareUndoAction( ev->x() );
@@ -361,7 +363,7 @@ void NotePropertiesRuler::propertyDragUpdate( QMouseEvent *ev )
Song *pSong = pHydrogen->getSong();
Instrument *pSelectedInstrument = pSong->getInstrumentList()->get( nSelectedInstrument );
- FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, nColumn ) {
+ FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, m_fTickPosition ) {
Note *pNote = it->second;
if ( pNote->get_instrument() != pSelectedInstrument && !m_selection.isSelected( pNote ) ) {
@@ -443,7 +445,7 @@ void NotePropertiesRuler::propertyDragUpdate( QMouseEvent *ev )
}
}
- m_nDragPreviousColumn = nColumn;
+ m_fDragPreviousColumn = m_fTickPosition;
Hydrogen::get_instance()->getSong()->setIsModified( true );
updateEditor();
@@ -564,7 +566,7 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
} else if ( ev->matches( QKeySequence::MoveToEndOfLine ) || ev->matches( QKeySequence::SelectEndOfLine ) ) {
// -->|
- m_pPatternEditorPanel->setCursorPosition( m_pPattern->get_length() );
+ m_pPatternEditorPanel->setCursorIndexPosition( m_pPattern->get_length() / granularity() );
} else if ( ev->matches( QKeySequence::MoveToPreviousChar ) || ev->matches( QKeySequence::SelectPreviousChar ) ) {
// <-
@@ -576,7 +578,7 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
} else if ( ev->matches( QKeySequence::MoveToStartOfLine ) || ev->matches( QKeySequence::SelectStartOfLine ) ) {
// |<--
- m_pPatternEditorPanel->setCursorPosition(0);
+ m_pPatternEditorPanel->setCursorIndexPosition(0);
} else {
// Value adjustments
@@ -626,7 +628,7 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
}
if ( fDelta != 0.0 || bRepeatLastValue ) {
- int column = m_pPatternEditorPanel->getCursorPosition();
+ double fColumn = m_pPatternEditorPanel->getCursorIndexPosition() * granularity();
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
Song *pSong = (Hydrogen::get_instance())->getSong();
int nNotes = 0;
@@ -639,10 +641,10 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
notes.push_back( pNote );
}
} else {
- FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, column ) {
+ FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, fColumn ) {
Note *pNote = it->second;
assert( pNote );
- assert( pNote->get_position() == column );
+ //assert( pNote->get_position() == column );
nNotes++;
notes.push_back( pNote );
}
@@ -657,7 +659,7 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
}
}
- prepareUndoAction( m_nMargin + column * m_nGridWidth );
+ prepareUndoAction( m_nMargin + fColumn * m_fGridWidth );
for ( Note *pNote : notes ) {
@@ -831,14 +833,14 @@ void NotePropertiesRuler::createVelocityBackground(QPixmap *pixmap)
QPainter p( pixmap );
- p.fillRect( 0, 0, m_nMargin + nNotes * m_nGridWidth, height(), backgroundColor );
+ p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
drawGridLines( p, Qt::DotLine );
// Horizontal lines at 10% intervals
p.setPen( horizLinesColor );
for (unsigned y = 0; y < m_nEditorHeight; y = y + (m_nEditorHeight / 10)) {
- p.drawLine( m_nMargin, y, 20 + nNotes * m_nGridWidth, y );
+ p.drawLine( m_nMargin, y, 20 + nNotes * m_fGridWidth, y );
}
// draw velocity lines
@@ -853,16 +855,16 @@ void NotePropertiesRuler::createVelocityBackground(QPixmap *pixmap)
FOREACH_NOTE_CST_IT_BEGIN_END(notes,it) {
Note *pposNote = it->second;
assert( pposNote );
- uint pos = pposNote->get_position();
+ double fPos = pposNote->get_position();
int xoffset = 0;
- FOREACH_NOTE_CST_IT_BOUND(notes,coit,pos) {
+ FOREACH_NOTE_CST_IT_BOUND(notes,coit, fPos) {
Note *pNote = coit->second;
assert( pNote );
if ( pNote->get_instrument() != pSong->getInstrumentList()->get( nSelectedInstrument )
&& !m_selection.isSelected( pNote ) ) {
continue;
}
- uint x_pos = m_nMargin + pos * m_nGridWidth;
+ uint x_pos = m_nMargin + round( fPos * m_fGridWidth );
uint line_end = height();
@@ -918,7 +920,7 @@ void NotePropertiesRuler::createPanBackground(QPixmap *pixmap)
if (m_pPattern) {
nNotes = m_pPattern->get_length();
}
- p.fillRect( 0, 0, m_nMargin + nNotes * m_nGridWidth, height(), backgroundColor );
+ p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
// central line
p.setPen( horizLinesColor );
@@ -937,9 +939,9 @@ void NotePropertiesRuler::createPanBackground(QPixmap *pixmap)
FOREACH_NOTE_CST_IT_BEGIN_END(notes,it) {
Note *pposNote = it->second;
assert( pposNote );
- uint pos = pposNote->get_position();
+ double fPos = pposNote->get_position();
int xoffset = 0;
- FOREACH_NOTE_CST_IT_BOUND(notes,coit,pos) {
+ FOREACH_NOTE_CST_IT_BOUND(notes,coit,fPos) {
Note *pNote = coit->second;
assert( pNote );
if ( pNote->get_note_off() || (pNote->get_instrument()
@@ -947,7 +949,8 @@ void NotePropertiesRuler::createPanBackground(QPixmap *pixmap)
&& !m_selection.isSelected( pNote ) ) ) {
continue;
}
- uint x_pos = m_nMargin + pNote->get_position() * m_nGridWidth;
+ uint x_pos = round( m_nMargin + ( fPos * m_fGridWidth ) );
+
QColor centerColor = DrumPatternEditor::computeNoteColor( pNote->get_velocity() );
p.setPen( Qt::NoPen );
@@ -1005,7 +1008,7 @@ void NotePropertiesRuler::createLeadLagBackground(QPixmap *pixmap)
if (m_pPattern) {
nNotes = m_pPattern->get_length();
}
- p.fillRect( 0, 0, m_nMargin + nNotes * m_nGridWidth, height(), backgroundColor );
+ p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
// central line
p.setPen( horizLinesColor );
@@ -1024,9 +1027,9 @@ void NotePropertiesRuler::createLeadLagBackground(QPixmap *pixmap)
FOREACH_NOTE_CST_IT_BEGIN_END(notes,it) {
Note *pposNote = it->second;
assert( pposNote );
- uint pos = pposNote->get_position();
+ double fPos = pposNote->get_position();
int xoffset = 0;
- FOREACH_NOTE_CST_IT_BOUND(notes,coit,pos) {
+ FOREACH_NOTE_CST_IT_BOUND(notes,coit,fPos) {
Note *pNote = coit->second;
assert( pNote );
if ( pNote->get_instrument() != pSong->getInstrumentList()->get( nSelectedInstrument )
@@ -1034,7 +1037,7 @@ void NotePropertiesRuler::createLeadLagBackground(QPixmap *pixmap)
continue;
}
- uint x_pos = m_nMargin + pNote->get_position() * m_nGridWidth;
+ uint x_pos = round( m_nMargin + ( fPos * m_fGridWidth ) );
int red1 = (int) (pNote->get_velocity() * 255);
int green1;
@@ -1114,13 +1117,13 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
}
QPainter p( pixmap );
- p.fillRect( 0, 0, m_nMargin + nNotes * m_nGridWidth, height(), backgroundColor );
+ p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
p.setPen( horizLinesColor );
for (unsigned y = 10; y < 80; y = y + 10 ) {
p.setPen( QPen( res_1, 1, Qt::DashLine ) );
if (y == 40) p.setPen( QPen( QColor(0,0,0), 1, Qt::SolidLine ) );
- p.drawLine( m_nMargin, y, m_nMargin + nNotes * m_nGridWidth, y );
+ p.drawLine( m_nMargin, y, m_nMargin + nNotes * m_fGridWidth, y );
}
for (unsigned y = 90; y < 210; y = y + 10 ) {
@@ -1128,7 +1131,7 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
if ( y == 100 ||y == 120 ||y == 140 ||y == 170 ||y == 190) {
p.setPen( QPen( QColor( 128, 128, 128 ), 9, Qt::SolidLine, Qt::FlatCap ) );
}
- p.drawLine( m_nMargin, y, m_nMargin + nNotes * m_nGridWidth, y );
+ p.drawLine( m_nMargin, y, m_nMargin + nNotes * m_fGridWidth, y );
}
// Annotate with note class names
@@ -1153,7 +1156,7 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
// Black outline each key
for (unsigned y = 90; y <= 210; y = y + 10 ) {
p.setPen( QPen( QColor( 0, 0, 0 ), 1, Qt::SolidLine));
- p.drawLine( m_nMargin, y-5, m_nMargin + nNotes * m_nGridWidth, y-5);
+ p.drawLine( m_nMargin, y-5, m_nMargin + nNotes * m_fGridWidth, y-5);
}
//paint the octave
@@ -1172,7 +1175,8 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
continue;
}
if ( !pNote->get_note_off() ) {
- uint x_pos = 17 + pNote->get_position() * m_nGridWidth;
+ uint x_pos = round( pNote->get_position() * m_fGridWidth );
+ x_pos += 17;
uint y_pos = (4-pNote->get_octave())*10-3;
p.setBrush(QColor( 99, 160, 233 ));
p.drawEllipse( x_pos, y_pos, 6, 6);
@@ -1199,7 +1203,8 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
if ( !pNote->get_note_off() ) {
int d = 8;
int k = pNote->get_key();
- uint x_pos = 16 + pNote->get_position() * m_nGridWidth;
+ uint x_pos = round ( ( pNote->get_position() ) * m_fGridWidth );
+ x_pos += 16;
uint y_pos = 200-(k*10)-4;
x_pos -= 1;
@@ -1240,10 +1245,10 @@ void NotePropertiesRuler::updateEditor( bool bPatternOnly )
// update editor width
if ( m_pPattern ) {
- m_nEditorWidth = m_nMargin + m_pPattern->get_length() * m_nGridWidth;
+ m_nEditorWidth = m_nMargin + m_pPattern->get_length() * m_fGridWidth;
}
else {
- m_nEditorWidth = m_nMargin + MAX_NOTES * m_nGridWidth;
+ m_nEditorWidth = m_nMargin + MAX_NOTES * m_fGridWidth;
}
if ( !m_bNeedsUpdate ) {
@@ -1276,13 +1281,13 @@ void NotePropertiesRuler::finishUpdateEditor()
if ( hasFocus() && ! HydrogenApp::get_instance()->hideKeyboardCursor() ) {
QPainter p( m_pBackground );
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_nGridWidth;
+ uint x = round( m_nMargin + m_pPatternEditorPanel->getCursorIndexPosition()* granularity() * m_fGridWidth ); // TODO check
QPen pen( Qt::black );
pen.setWidth( 2 );
p.setPen( pen );
p.setRenderHint( QPainter::Antialiasing );
- p.drawRoundedRect( QRect( x-m_nGridWidth*3, 0+1, m_nGridWidth*6, height()-2 ), 4, 4 );
+ p.drawRoundedRect( QRect( x-m_fGridWidth*3, 0+1, m_fGridWidth*6, height()-2 ), 4, 4 );
}
// redraw all
@@ -1327,7 +1332,7 @@ std::vector NotePropertiesRuler::elementsIn
}
int pos = it->first;
- uint x_pos = m_nMargin + pos * m_nGridWidth;
+ uint x_pos = m_nMargin + pos * m_fGridWidth;
if ( r.intersects( QRect( x_pos, 0, 1, height() ) ) ) {
result.push_back( it->second );
}
@@ -1343,8 +1348,10 @@ std::vector NotePropertiesRuler::elementsIn
///
QRect NotePropertiesRuler::getKeyboardCursorRect()
{
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_nGridWidth;
- return QRect( x-m_nGridWidth*3, 0+1, m_nGridWidth*6, height()-2 );
+ uint x = round( m_nMargin + m_pPatternEditorPanel->getCursorIndexPosition()* granularity() * m_fGridWidth ); // TODO check
+ int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
+ uint y = nSelectedInstrument * m_nGridHeight;
+ return QRect( x-m_fGridWidth*3, 0+1, m_fGridWidth*6, height()-2 );
}
void NotePropertiesRuler::selectAll() {
diff --git a/src/gui/src/PatternEditor/NotePropertiesRuler.h b/src/gui/src/PatternEditor/NotePropertiesRuler.h
index 67b7ac01e..267505754 100644
--- a/src/gui/src/PatternEditor/NotePropertiesRuler.h
+++ b/src/gui/src/PatternEditor/NotePropertiesRuler.h
@@ -136,7 +136,7 @@ class NotePropertiesRuler : public PatternEditor
void adjustNotePropertyDelta( H2Core::Note *pNote, float fDelta, bool bMessage = false );
- int m_nDragPreviousColumn;
+ double m_fDragPreviousColumn;
};
diff --git a/src/gui/src/PatternEditor/PatternEditor.cpp b/src/gui/src/PatternEditor/PatternEditor.cpp
index 3de820bdf..913a6c741 100644
--- a/src/gui/src/PatternEditor/PatternEditor.cpp
+++ b/src/gui/src/PatternEditor/PatternEditor.cpp
@@ -54,8 +54,11 @@ const char* PatternEditor::__class_name = "PatternEditor";
PatternEditor::PatternEditor( QWidget *pParent, const char *sClassName,
PatternEditorPanel *panel )
: Object ( sClassName ), QWidget( pParent ), m_selection( this ) {
- m_nResolution = 8;
- m_bUseTriplets = false;
+ m_nTupletNumerator = Preferences::get_instance()->getPatternEditorGridTupletNumerator();
+ m_nTupletDenominator = Preferences::get_instance()->getPatternEditorGridTupletDenominator();
+ m_nResolution = 8; //TODO where is this update to match preferences?
+ //m_nTupletNumerator = 4;
+ //m_nTupletDenominator = 4;
m_pDraggedNote = nullptr;
m_pPatternEditorPanel = panel;
m_pPattern = nullptr;
@@ -63,8 +66,9 @@ PatternEditor::PatternEditor( QWidget *pParent, const char *sClassName,
m_bFineGrained = false;
m_bCopyNotMove = false;
- m_nGridWidth = Preferences::get_instance()->getPatternEditorGridWidth();
- m_nEditorWidth = m_nMargin + m_nGridWidth * ( MAX_NOTES * 4 );
+ m_fGridWidth = Preferences::get_instance()->getPatternEditorGridWidth();
+ /* default width is four 4/4 bars, in pixels units */
+ m_nEditorWidth = m_nMargin + m_fGridWidth * ( MAX_NOTES * 4 );
setFocusPolicy(Qt::StrongFocus);
@@ -85,33 +89,41 @@ PatternEditor::PatternEditor( QWidget *pParent, const char *sClassName,
}
-void PatternEditor::setResolution(uint res, bool bUseTriplets)
+void PatternEditor::setResolution( uint res )
{
- this->m_nResolution = res;
- this->m_bUseTriplets = bUseTriplets;
+ m_nResolution = res;
// redraw all
update( 0, 0, width(), height() );
m_pPatternEditorPanel->updateEditors();
}
+void PatternEditor::setTupletRatio( int nTupletNumerator, int nTupletDenominator ) {
+ m_nTupletNumerator = nTupletNumerator;
+ m_nTupletDenominator = nTupletDenominator;
+
+ // redraw all
+ update( 0, 0, width(), height() );
+ m_pPatternEditorPanel->updateEditors();
+}
+
void PatternEditor::zoomIn()
{
- if (m_nGridWidth >= 3) {
- m_nGridWidth *= 2;
+ if ( m_fGridWidth >= 3. ) {
+ m_fGridWidth *= 2.;
} else {
- m_nGridWidth *= 1.5;
+ m_fGridWidth *= 1.5;
}
updateEditor();
}
void PatternEditor::zoomOut()
{
- if ( m_nGridWidth > 1.5 ) {
- if (m_nGridWidth > 3) {
- m_nGridWidth /= 2;
+ if ( m_fGridWidth > 1.5 ) {
+ if ( m_fGridWidth > 3. ) {
+ m_fGridWidth /= 2.;
} else {
- m_nGridWidth /= 1.5;
+ m_fGridWidth /= 1.5;
}
updateEditor();
}
@@ -176,8 +188,8 @@ void PatternEditor::drawNoteSymbol( QPainter &p, QPoint pos, H2Core::Note *pNote
if ( bMoving ) {
movingPen.setStyle( Qt::DotLine );
movingPen.setWidth( 2 );
- QPoint delta = movingGridOffset();
- movingOffset = QPoint( delta.x() * m_nGridWidth,
+ QPointF delta = movingGridOffset();
+ movingOffset = QPoint( delta.x() * m_fGridWidth,
delta.y() * m_nGridHeight );
}
@@ -192,7 +204,7 @@ void PatternEditor::drawNoteSymbol( QPainter &p, QPoint pos, H2Core::Note *pNote
if ( pNote->get_length() != -1 ) {
float fNotePitch = pNote->get_octave() * 12 + pNote->get_key();
float fStep = pow( 1.0594630943593, ( double )fNotePitch );
- width = m_nGridWidth * pNote->get_length() / fStep;
+ width = m_fGridWidth * pNote->get_length() / fStep;
width = width - 1; // lascio un piccolo spazio tra una nota ed un altra
if ( bSelected ) {
@@ -241,22 +253,37 @@ void PatternEditor::drawNoteSymbol( QPainter &p, QPoint pos, H2Core::Note *pNote
}
-int PatternEditor::getColumn( int x, bool bUseFineGrained ) const
-{
- int nGranularity = 1;
- if ( !( bUseFineGrained && m_bFineGrained ) ) {
- nGranularity = granularity();
+double PatternEditor::getColumn( int x, bool bUseFineGrained ) const
+{ // without fineGrain, returns the position of the nearest grid mark, in tick units (rounded value!)
+ // with fineGrain, returns the position of nearest tick with res = 192/whole note
+
+ if ( x <= m_nMargin ) {
+ return 0;
+ } else {
+ double fGranularity;
+ if ( bUseFineGrained && m_bFineGrained ) {
+ fGranularity = 1.;
+ } else {
+ fGranularity = granularity();
+ }
+
+ double fWidth = m_fGridWidth * fGranularity; // distance between grid marks (or ticks), in pixel units
+ double fGridIndex = round( ( x - m_nMargin ) / fWidth ); // The index of the nearest grid mark (or tick)
+ return fGridIndex * fGranularity; // the position of the nearest grid mark (or tick), in tick units
}
- int nWidth = m_nGridWidth * nGranularity;
- int nColumn = ( x - m_nMargin + (nWidth / 2) ) / nWidth;
- nColumn = nColumn * nGranularity;
- if ( nColumn < 0 ) {
+}
+
+int PatternEditor::getGridIndex( int x ) const {
+ if ( x <= m_nMargin ) {
return 0;
} else {
- return nColumn;
+ float fWidth = m_fGridWidth * granularity(); // distance between grid marks, in pixel units
+ int nGridIndex = round( ( x - m_nMargin ) / fWidth ); // The index of the nearest grid mark
+ return nGridIndex;
}
}
+
void PatternEditor::selectNone()
{
m_selection.clearSelection();
@@ -276,18 +303,19 @@ void PatternEditor::copy()
XMLNode positionNode = selection.createNode( "sourcePosition" );
bool bWroteNote = false;
// "Top left" of selection, in the three dimensional time*instrument*pitch space.
- int nLowestPos, nLowestInstrument, nHighestPitch;
+ double fLowestPos;
+ int nLowestInstrument, nHighestPitch;
for ( Note *pNote : m_selection ) {
int nPitch = pNote->get_notekey_pitch() + 12*OCTAVE_OFFSET;
- int nPos = pNote->get_position();
+ double fPos = pNote->get_position();
int nInstrument = pInstrumentList->index( pNote->get_instrument() );
if ( bWroteNote ) {
- nLowestPos = std::min( nPos, nLowestPos );
+ fLowestPos = std::min( fPos, fLowestPos );
nLowestInstrument = std::min( nInstrument, nLowestInstrument );
nHighestPitch = std::max( nPitch, nHighestPitch );
} else {
- nLowestPos = nPos;
+ fLowestPos = fPos;
nLowestInstrument = nInstrument;
nHighestPitch = nPitch;
bWroteNote = true;
@@ -297,11 +325,11 @@ void PatternEditor::copy()
}
if ( bWroteNote ) {
- positionNode.write_int( "position", nLowestPos );
+ positionNode.write_double( "position", fLowestPos );
positionNode.write_int( "instrument", nLowestInstrument );
positionNode.write_int( "note", nHighestPitch );
} else {
- positionNode.write_int( "position", m_pPatternEditorPanel->getCursorPosition() );
+ positionNode.write_int( "index_position", m_pPatternEditorPanel->getCursorIndexPosition() );
positionNode.write_int( "instrument", pHydrogen->getSelectedInstrumentNumber() );
}
@@ -392,7 +420,7 @@ void PatternEditor::updateModifiers( QInputEvent *ev ) {
bool PatternEditor::notesMatchExactly( Note *pNoteA, Note *pNoteB ) const {
return ( pNoteA->match( pNoteB->get_instrument(), pNoteB->get_key(), pNoteB->get_octave() )
- && pNoteA->get_position() == pNoteB->get_position()
+ && fabs( pNoteA->get_position() - pNoteB->get_position() ) < POS_EPSILON
&& pNoteA->get_velocity() == pNoteB->get_velocity()
&& pNoteA->get_pan_r() == pNoteB->get_pan_r()
&& pNoteA->get_pan_l() == pNoteB->get_pan_l()
@@ -464,14 +492,16 @@ void PatternEditor::deselectAndOverwriteNotes( std::vector< H2Core::Note *> &sel
for ( auto pSelectedNote : selected ) {
m_selection.removeFromSelection( pSelectedNote, /* bCheck=*/false );
bool bFoundExact = false;
- int nPosition = pSelectedNote->get_position();
- for ( auto it = pNotes->lower_bound( nPosition ); it != pNotes->end() && it->first == nPosition; ) {
+ double fPosition = pSelectedNote->get_position();
+ for ( auto it = pNotes->lower_bound( fPosition - POS_EPSILON );
+ it != pNotes->end() && it->first < fPosition + POS_EPSILON; ) { // NOTE: counter not incremented here!
Note *pNote = it->second;
if ( !bFoundExact && notesMatchExactly( pNote, pSelectedNote ) ) {
// Found an exact match. We keep this.
bFoundExact = true;
++it;
- } else if ( pSelectedNote->match( pNote ) && pNote->get_position() == pSelectedNote->get_position() ) {
+ } else if ( pSelectedNote->match( pNote ) // match key, octave & instrument
+ && fabs( pNote->get_position() - pSelectedNote->get_position() ) < POS_EPSILON ) {
// Something else occupying the same position (which may or may not be an exact duplicate)
it = pNotes->erase( it );
} else {
@@ -526,25 +556,23 @@ void PatternEditor::updatePatternInfo() {
}
-QPoint PatternEditor::movingGridOffset( ) const {
+QPointF PatternEditor::movingGridOffset( ) const {
QPoint rawOffset = m_selection.movingOffset();
- // Quantize offset to multiples of m_nGrid{Width,Height}
- int nQuantX = m_nGridWidth, nQuantY = m_nGridHeight;
- float nFactor = 1;
+ // Quantize offset to multiples of m_fGrid{Width,Height}
+ double fQuantX = m_fGridWidth;
+ int nQuantY = m_nGridHeight;
+ double fFactor = 1;
if ( ! m_bFineGrained ) {
- nFactor = granularity();
- nQuantX = m_nGridWidth * nFactor;
+ fFactor = granularity();
+ fQuantX = m_fGridWidth * fFactor;
}
- int x_bias = nQuantX / 2, y_bias = nQuantY / 2;
+ int y_bias = nQuantY / 2;
if ( rawOffset.y() < 0 ) {
y_bias = -y_bias;
}
- if ( rawOffset.x() < 0 ) {
- x_bias = -x_bias;
- }
- int x_off = (rawOffset.x() + x_bias) / nQuantX;
+ double x_off = round( rawOffset.x() / fQuantX );
int y_off = (rawOffset.y() + y_bias) / nQuantY;
- return QPoint( nFactor * x_off, y_off);
+ return QPointF( fFactor * x_off, y_off);
}
@@ -570,14 +598,14 @@ void PatternEditor::drawGridLines( QPainter &p, Qt::PenStyle style ) const
pStyle->m_patternEditor_line5Color.getBlue() ),
};
- int nGranularity = granularity() * m_nResolution;
+ int nGranularity = round( granularity() * m_nResolution );
int nNotes = MAX_NOTES;
if ( m_pPattern ) {
nNotes = m_pPattern->get_length();
}
- int nMaxX = m_nGridWidth * nNotes + m_nMargin;
+ int nMaxX = m_fGridWidth * nNotes + m_nMargin;
- if ( !m_bUseTriplets ) {
+ if ( m_nTupletNumerator == 4 && m_nTupletDenominator == 4 ) { // every other case is drawn in tuplet mode
// Draw vertical lines. To minimise pen colour changes (and
// avoid unnecessary division operations), we draw them in
@@ -591,7 +619,7 @@ void PatternEditor::drawGridLines( QPainter &p, Qt::PenStyle style ) const
// | . : . | . : . | . : . | . : . - third pass, odd 1/16th notes
uint nRes = 4;
- uint nStep = nGranularity / nRes * m_nGridWidth;
+ uint nStep = nGranularity / nRes * m_fGridWidth;
// First, quarter note markers. All the quarter note markers must be drawn.
if ( m_nResolution >= nRes ) {
@@ -615,21 +643,27 @@ void PatternEditor::drawGridLines( QPainter &p, Qt::PenStyle style ) const
nRes *= 2;
nStep /= 2;
}
-
+
} else {
- // Triplet style markers, we only differentiate colours on the
- // first of every triplet.
- uint nStep = granularity() * m_nGridWidth;
+ // Tuplets style markers, we only differentiate colours on the
+ // first of every tuplet.
+ float fStep = granularity() * m_fGridWidth;
p.setPen( QPen( res[ 0 ], 0, style ) );
- for ( uint x = m_nMargin; x < nMaxX; x += nStep * 3 ) {
- p.drawLine(x, 1, x, m_nEditorHeight - 1);
+ for ( float x = m_nMargin; x < nMaxX; x += fStep * m_nTupletNumerator ) {
+ p.drawLine( x, 1, x, m_nEditorHeight - 1);
}
- // Second and third marks
+ // Second, third... n-th marks
p.setPen( QPen( res[ 2 ], 0, style ) );
- for ( uint x = m_nMargin + nStep; x < nMaxX; x += nStep * 3 ) {
- p.drawLine(x, 1, x, m_nEditorHeight - 1);
- p.drawLine(x + nStep, 1, x + nStep, m_nEditorHeight - 1);
+ for ( float x0 = m_nMargin; x0 < nMaxX; x0 += fStep * m_nTupletNumerator ) {
+ for ( uint i = 1; i < m_nTupletNumerator; i++ ) {
+ int x = round( x0 + i * fStep );
+ if ( x < nMaxX ) {
+ p.drawLine( x, 1, x, m_nEditorHeight - 1 );
+ } else {
+ i = m_nTupletNumerator; // trick to break
+ }
+ }
}
}
diff --git a/src/gui/src/PatternEditor/PatternEditor.h b/src/gui/src/PatternEditor/PatternEditor.h
index 06ee7b917..fbad0476a 100644
--- a/src/gui/src/PatternEditor/PatternEditor.h
+++ b/src/gui/src/PatternEditor/PatternEditor.h
@@ -67,12 +67,27 @@ class PatternEditor : public QWidget,
//! Set the editor grid resolution, dividing a whole note into `res` subdivisions.
- void setResolution( uint res, bool bUseTriplets );
+ void setResolution( uint res );
uint getResolution() const { return m_nResolution; }
- bool isUsingTriplets() const { return m_bUseTriplets; }
+
+ //void setTupletNumerator( int n ) { m_nTupletNumerator = n; }
+ int getTupletNumerator() const { return m_nTupletNumerator; }
+
+ //void setTupletDenominator( int n ) { m_nTupletDenominator = n; }
+ int getTupletDenominator() const { return m_nTupletDenominator; }
+
+ // tuplet numerator and denominator should be set together
+ void setTupletRatio( int nTupletNumerator, int nTupletDenominator );
- float getGridWidth() const { return m_nGridWidth; }
+ float getGridWidth() const { return m_fGridWidth; }
unsigned getGridHeight() const { return m_nGridHeight; }
+
+ void setTupletResolution( int nRes, int nTupletNum, int nTupletDen) { //TODO needed?
+ m_nResolution = nRes;
+ m_nTupletNumerator = nTupletNum;
+ m_nTupletDenominator = nTupletDen;
+ }
+
//! Zoom in / out on the time axis
void zoomIn();
void zoomOut();
@@ -150,31 +165,63 @@ public slots:
protected:
- //! Granularity of grid positioning (in ticks)
- int granularity() const {
- int nBase;
- if (m_bUseTriplets) {
- nBase = 3;
- }
- else {
- nBase = 4;
- }
- return 4 * MAX_NOTES / ( nBase * m_nResolution );
+ //! Granularity of grid positioning ( = distance between grid marks), in tick units
+ double granularity() const { // float for tuplets
+ return (double) MAX_NOTES * m_nTupletDenominator / ( m_nTupletNumerator * m_nResolution );
}
uint m_nEditorHeight;
uint m_nEditorWidth;
-
- float m_nGridWidth;
+
+ /* the width of a tick on the screen (whose duration is defined: whole note / MAX_NOTES ) in pixel units.
+ * it depends on zoom.
+ */
+ float m_fGridWidth;
+
unsigned m_nGridHeight;
int m_nSelectedPatternNumber;
H2Core::Pattern *m_pPattern;
+ /* use it to add a left margin in the editor, before the first tick */
const int m_nMargin = 20;
+ /** the inverse of grid quantum duration in whole notes
+ * e.g. quantum = 1/16 of whole note <=> resolution = 16
+ * Ideally one could set any (fractional) resolution, but the GUI doesn't allow this:
+ * possible values are only powers of 2 in the GUI (or MAX_NOTES if resolution is set to off)
+ *
+ * comment by oddtime:
+ * It would be so cool entering resolutions like 12 (8th triplets) or 20 (16ths quintuplets)
+ * or 3/20 (quarter 5:3 tuplets)...!!!
+ * However the same result is possible with tuplets, accordingly to music notation style.
+ */
+ //TODO should next 3 members be here or only in preferences?
uint m_nResolution;
- bool m_bUseTriplets;
+
+ /** Tuplet notation is used to represent ANY rational note value in whole notes, using the std music symbols
+ * (quarters, 8ths, 16ths...).
+ * A tuplet is explicitly specified by a rational number: the fraction = m_nTupletNumerator / m_nTupletDenominator,
+ * in fact this fraction DIVIDES the note value returning its resultant length (in whole note units).
+ *
+ * Example: for standard triplets the ratio is 3:2,
+ * in fact a single 1/8 note under a triplet has length = 1/8 * 2/3 of whole note = 1/12 of whole note.
+ * Other examples: standard quintuplets: 5:4;
+ * weird (wrongly written?) quintuplets: 5:2;
+ * quartuplets in compound meters: 4:3;
+ * a difficult tuplet: 5:7.
+ *
+ * Note: when the TupletDenominator is hidden, a power of 2 is usually assumed for it (the biggest but not bigger
+ * than TupletNumerator) except for quartuplets or 2-tuplets (in those cases there isn't a more used assumed
+ * TupletDenominator).
+ * Note: since the music symbols provides all the (inverse) powers of 2 (quarters, 8ths, 16ths...)
+ * plus a sum operator (the tie!), the tuplet denominator (which becomes a numerator ;) ) is actually REDUNDANT
+ * to get any rational note duration in whole notes,
+ * BUT music notation provides it and user may benefit from it (tuplet is more clear with the explicit ratio).
+ */
+ int m_nTupletNumerator;
+ int m_nTupletDenominator;
+
bool m_bFineGrained;
bool m_bCopyNotMove;
@@ -186,8 +233,13 @@ public slots:
PatternEditorPanel *m_pPatternEditorPanel;
QMenu *m_pPopupMenu;
- int getColumn( int x, bool bUseFineGrained = false ) const;
- QPoint movingGridOffset() const;
+ // Magnetic conversions (quantized by the grid granularity)
+ /* from the pixel position to the position of the nearest grid mark, in tick units */
+ double getColumn( int x, bool bUseFineGrained = false ) const;
+ /* from the pixel position to the index of the nearest grid mark */
+ int getGridIndex( int x ) const;
+
+ QPointF movingGridOffset() const;
//! Draw lines for note grid.
void drawGridLines( QPainter &p, Qt::PenStyle style = Qt::SolidLine ) const;
diff --git a/src/gui/src/PatternEditor/PatternEditorInstrumentList.cpp b/src/gui/src/PatternEditor/PatternEditorInstrumentList.cpp
index 63ed9af59..f6bcbcf7d 100644
--- a/src/gui/src/PatternEditor/PatternEditorInstrumentList.cpp
+++ b/src/gui/src/PatternEditor/PatternEditorInstrumentList.cpp
@@ -114,7 +114,9 @@ InstrumentLine::InstrumentLine(QWidget* pParent)
m_pFunctionPopupSub->addAction( tr( "Fill 1/2 notes" ), this, SLOT( functionFillEveryTwoNotes() ) );
m_pFunctionPopupSub->addAction( tr( "Fill 1/3 notes" ), this, SLOT( functionFillEveryThreeNotes() ) );
m_pFunctionPopupSub->addAction( tr( "Fill 1/4 notes" ), this, SLOT( functionFillEveryFourNotes() ) );
+ m_pFunctionPopupSub->addAction( tr( "Fill 1/5 notes" ), this, SLOT( functionFillEveryFiveNotes() ) );
m_pFunctionPopupSub->addAction( tr( "Fill 1/6 notes" ), this, SLOT( functionFillEverySixNotes() ) );
+ m_pFunctionPopupSub->addAction( tr( "Fill 1/7 notes" ), this, SLOT( functionFillEverySevenNotes() ) );
m_pFunctionPopupSub->addAction( tr( "Fill 1/8 notes" ), this, SLOT( functionFillEveryEightNotes() ) );
m_pFunctionPopupSub->addAction( tr( "Fill 1/12 notes" ), this, SLOT( functionFillEveryTwelveNotes() ) );
m_pFunctionPopupSub->addAction( tr( "Fill 1/16 notes" ), this, SLOT( functionFillEverySixteenNotes() ) );
@@ -373,7 +375,9 @@ void InstrumentLine::functionFillAllNotes(){ functionFillNotes(1); }
void InstrumentLine::functionFillEveryTwoNotes(){ functionFillNotes(2); }
void InstrumentLine::functionFillEveryThreeNotes(){ functionFillNotes(3); }
void InstrumentLine::functionFillEveryFourNotes(){ functionFillNotes(4); }
+void InstrumentLine::functionFillEveryFiveNotes(){ functionFillNotes(5); }
void InstrumentLine::functionFillEverySixNotes(){ functionFillNotes(6); }
+void InstrumentLine::functionFillEverySevenNotes(){ functionFillNotes(7); }
void InstrumentLine::functionFillEveryEightNotes(){ functionFillNotes(8); }
void InstrumentLine::functionFillEveryTwelveNotes(){ functionFillNotes(12); }
void InstrumentLine::functionFillEverySixteenNotes(){ functionFillNotes(16); }
@@ -383,33 +387,26 @@ void InstrumentLine::functionFillNotes( int every )
Hydrogen *pHydrogen = Hydrogen::get_instance();
PatternEditorPanel *pPatternEditorPanel = HydrogenApp::get_instance()->getPatternEditorPanel();
- DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
- int nBase;
- if ( pPatternEditor->isUsingTriplets() ) {
- nBase = 3;
- }
- else {
- nBase = 4;
- }
- int nResolution = 4 * MAX_NOTES * every / ( nBase * pPatternEditor->getResolution() );
-
+ //DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
Song *pSong = pHydrogen->getSong();
- QStringList notePositions;
+ std::vector notePositions;
Pattern* pCurrentPattern = getCurrentPattern();
- if (pCurrentPattern != nullptr) {
+ if ( pCurrentPattern != nullptr ) {
int nPatternSize = pCurrentPattern->get_length();
int nSelectedInstrument = pHydrogen->getSelectedInstrumentNumber();
- if (nSelectedInstrument != -1) {
+ if ( nSelectedInstrument != -1 ) {
Instrument *instrRef = (pSong->getInstrumentList())->get( nSelectedInstrument );
- for (int i = 0; i < nPatternSize; i += nResolution) {
+ int i = 0;
+ double fColumn = 0;
+ while ( fColumn < nPatternSize) {
bool noteAlreadyPresent = false;
const Pattern::notes_t* notes = pCurrentPattern->get_notes();
- FOREACH_NOTE_CST_IT_BOUND(notes,it,i) {
+ FOREACH_NOTE_CST_IT_BOUND( notes, it, fColumn ) {
Note *pNote = it->second;
if ( pNote->get_instrument() == instrRef ) {
// note already exists
@@ -419,10 +416,16 @@ void InstrumentLine::functionFillNotes( int every )
}
if ( noteAlreadyPresent == false ) {
- notePositions << QString("%1").arg(i);
+ //notePositions << QString("%1").arg( nColumn );
+ notePositions.push_back( fColumn );
}
- }
- SE_fillNotesRightClickAction *action = new SE_fillNotesRightClickAction( notePositions, nSelectedInstrument, pHydrogen->getSelectedPatternNumber() );
+
+ i += every;
+ fColumn = i * pPatternEditorPanel->granularity();
+ }
+ SE_fillNotesRightClickAction *action = new SE_fillNotesRightClickAction( notePositions,
+ nSelectedInstrument,
+ pHydrogen->getSelectedPatternNumber() );
HydrogenApp::get_instance()->m_pUndoStack->push( action );
}
}
@@ -431,45 +434,37 @@ void InstrumentLine::functionFillNotes( int every )
-void InstrumentLine::functionRandomizeVelocity()
+void InstrumentLine::functionRandomizeVelocity() // TODO should it act only on the notes on the grid marks (magnetically)?
{
Hydrogen *pHydrogen = Hydrogen::get_instance();
PatternEditorPanel *pPatternEditorPanel = HydrogenApp::get_instance()->getPatternEditorPanel();
- DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
+ //DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
-
- int nBase;
- if ( pPatternEditor->isUsingTriplets() ) {
- nBase = 3;
- }
- else {
- nBase = 4;
- }
- int nResolution = 4 * MAX_NOTES / ( nBase * pPatternEditor->getResolution() );
+ float fResolution = pPatternEditorPanel-> granularity();
Song *pSong = pHydrogen->getSong();
- QStringList noteVeloValue;
+ QStringList noteVeloValue; // TODO why a QSTringList instead of std::vector ? Does it save space?
QStringList oldNoteVeloValue;
Pattern* pCurrentPattern = getCurrentPattern();
- if (pCurrentPattern != nullptr) {
+ if ( pCurrentPattern != nullptr ) {
int nPatternSize = pCurrentPattern->get_length();
int nSelectedInstrument = pHydrogen->getSelectedInstrumentNumber();
- if (nSelectedInstrument != -1) {
- Instrument *instrRef = (pSong->getInstrumentList())->get( nSelectedInstrument );
+ if ( nSelectedInstrument != -1 ) {
+ Instrument *instrRef = ( pSong->getInstrumentList() )->get( nSelectedInstrument );
- for (int i = 0; i < nPatternSize; i += nResolution) {
+ for ( int i = 0; i*fResolution < nPatternSize; i++ ) { //TODO while() loop using variable float fTickPosition
const Pattern::notes_t* notes = pCurrentPattern->get_notes();
- FOREACH_NOTE_CST_IT_BOUND(notes,it,i) {
+ FOREACH_NOTE_CST_IT_BOUND( notes, it, i*fResolution ) {
Note *pNote = it->second;
if ( pNote->get_instrument() == instrRef ) {
- float fVal = ( rand() % 100 ) / 100.0;
+ float fVal = ( rand() % 100 ) / 100.0; // uses quantized 0.01 steps
oldNoteVeloValue << QString("%1").arg( pNote->get_velocity() );
fVal = pNote->get_velocity() + ( ( fVal - 0.50 ) / 2 );
- if ( fVal < 0 ) {
+ if ( fVal < 0 ) {
fVal = 0;
}
if ( fVal > 1 ) {
diff --git a/src/gui/src/PatternEditor/PatternEditorInstrumentList.h b/src/gui/src/PatternEditor/PatternEditorInstrumentList.h
index 99668ca69..ab3d62a77 100644
--- a/src/gui/src/PatternEditor/PatternEditorInstrumentList.h
+++ b/src/gui/src/PatternEditor/PatternEditorInstrumentList.h
@@ -29,6 +29,7 @@
#include
#include
+#include
#include
#include "../Widgets/PixmapWidget.h"
@@ -61,11 +62,14 @@ class InstrumentLine : public PixmapWidget
private slots:
void functionClearNotes();
+ // TODO use parametric fillNotes(every) with Qvariant in menu item?
void functionFillAllNotes();
void functionFillEveryTwoNotes();
void functionFillEveryThreeNotes();
void functionFillEveryFourNotes();
+ void functionFillEveryFiveNotes();
void functionFillEverySixNotes();
+ void functionFillEverySevenNotes();
void functionFillEveryEightNotes();
void functionFillEveryTwelveNotes();
void functionFillEverySixteenNotes();
diff --git a/src/gui/src/PatternEditor/PatternEditorPanel.cpp b/src/gui/src/PatternEditor/PatternEditorPanel.cpp
index cca8a245f..7efc1ef84 100644
--- a/src/gui/src/PatternEditor/PatternEditorPanel.cpp
+++ b/src/gui/src/PatternEditor/PatternEditorPanel.cpp
@@ -71,10 +71,19 @@ PatternEditorPanel::PatternEditorPanel( QWidget *pParent )
{
setAcceptDrops(true);
- Preferences *pPref = Preferences::get_instance();
+ m_nCursorIndexPosition = 0;
- m_nCursorPosition = 0;
- m_nCursorIncrement = 0;
+
+ Preferences *pPref = Preferences::get_instance();
+ if ( pPref ) { // TODO need this statement?
+ m_nResolution = pPref->getPatternEditorGridResolution();
+ m_nTupletNumerator = pPref->getPatternEditorGridTupletNumerator();
+ m_nTupletDenominator = pPref->getPatternEditorGridTupletDenominator();
+ } else { // default values
+ m_nResolution = 8;
+ m_nTupletNumerator = 4;
+ m_nTupletDenominator = 4;
+ }
// Editor TOP
PixmapWidget *editor_top = new PixmapWidget( nullptr );
@@ -106,7 +115,7 @@ PatternEditorPanel::PatternEditorPanel( QWidget *pParent )
//wolke some background images back_size_res
PixmapWidget *pSizeResol = new PixmapWidget( nullptr );
- pSizeResol->setFixedSize( 216, 20 );
+ pSizeResol->setFixedSize( 300, 20 );
pSizeResol->setPixmap( "/patternEditor/background_res-new.png" );
pSizeResol->move( 0, 3 );
editor_top_hbox_2->addWidget( pSizeResol );
@@ -156,7 +165,12 @@ PatternEditorPanel::PatternEditorPanel( QWidget *pParent )
__resolution_combo->move( 154, 2 );
// is triggered from inside PatternEditorPanel()
connect( __resolution_combo, SIGNAL( valueChanged( int ) ), this, SLOT( gridResolutionChanged( int ) ) );
-
+
+ // TUPLET LCD
+ m_pTupletLCD = new LCDDisplay( pSizeResol, LCDDigit::SMALL_BLUE, 5 );
+ m_pTupletLCD->move( 252, 2 );
+ m_pTupletLCD->setToolTip( tr( "Select resolution Tuplet" ) );
+ connect( m_pTupletLCD, SIGNAL( displayClicked( LCDDisplay* ) ), this, SLOT( tupletLCDClicked() ) );
PixmapWidget *pRec = new PixmapWidget( nullptr );
pRec->setFixedSize( 300, 20 );
@@ -525,11 +539,11 @@ PatternEditorPanel::PatternEditorPanel( QWidget *pParent )
// restore grid resolution
int nIndex;
- int nRes = pPref->getPatternEditorGridResolution();
- if ( nRes == MAX_NOTES ) {
+
+ if ( m_nResolution == MAX_NOTES ) {
nIndex = 11;
- } else if ( pPref->isPatternEditorUsingTriplets() == false ) {
- switch ( nRes ) {
+ } else if ( m_nTupletNumerator != 3 || m_nTupletDenominator != 2 ) {
+ switch ( m_nResolution ) {
case 4: nIndex = 0; break;
case 8: nIndex = 1; break;
case 16: nIndex = 2; break;
@@ -537,20 +551,26 @@ PatternEditorPanel::PatternEditorPanel( QWidget *pParent )
case 64: nIndex = 4; break;
default:
nIndex = 0;
- ERRORLOG( QString( "Wrong grid resolution: %1" ).arg( pPref->getPatternEditorGridResolution() ) );
+ ERRORLOG( QString( "Wrong grid resolution: %1" ).arg( m_nResolution ) );
}
} else {
- switch ( nRes ) {
- case 8: nIndex = 6; break;
- case 16: nIndex = 7; break;
- case 32: nIndex = 8; break;
- case 64: nIndex = 9; break;
+ switch ( m_nResolution ) {
+ case 4: nIndex = 6; break;
+ case 8: nIndex = 7; break;
+ case 16: nIndex = 8; break;
+ case 32: nIndex = 9; break;
default:
nIndex = 6;
- ERRORLOG( QString( "Wrong grid resolution: %1" ).arg( pPref->getPatternEditorGridResolution() ) );
+ ERRORLOG( QString( "Wrong grid resolution: %1" ).arg( m_nResolution ) );
}
}
__resolution_combo->select( nIndex );
+
+ if ( m_nTupletNumerator == 4 && m_nTupletDenominator == 4 ) {
+ m_pTupletLCD->setText( tr( "off" ) );
+ } else {
+ m_pTupletLCD->setText( QString("%1:%2").arg( m_nTupletNumerator ).arg( m_nTupletDenominator ) );
+ }
// LAYOUT
QVBoxLayout *pVBox = new QVBoxLayout();
@@ -627,40 +647,65 @@ void PatternEditorPanel::on_patternEditorHScroll( int nValue )
-void PatternEditorPanel::gridResolutionChanged( int nSelected )
-{
- int nResolution;
- bool bUseTriplets = false;
-
+void PatternEditorPanel::gridResolutionChanged( int nSelected ) {
+ double fOldCursorTickPosition = getCursorFloatPosition();
if ( nSelected == 11 ) {
- nResolution = MAX_NOTES;
+ m_nResolution = MAX_NOTES;
+ m_nTupletNumerator = 4;
+ m_nTupletDenominator = 4;
+
+ setTupletRatioToAllEditors( m_nTupletNumerator, m_nTupletDenominator );
+ // TODO why all the editors have the same grid members and there is no a unique variable for them to read?
+
+ m_pTupletLCD->setText( QString( "%1:%2" ).arg( m_nTupletNumerator ).arg( m_nTupletDenominator ) );
+ Preferences::get_instance()->setPatternEditorGridTupletRatio( m_nTupletNumerator, m_nTupletDenominator );
}
else if ( nSelected > 4 ) {
- bUseTriplets = true;
- nResolution = 0x1 << ( nSelected - 3 );
+ m_nTupletNumerator = 3;
+ m_nTupletDenominator = 2;
+ m_nResolution = 0x1 << ( nSelected - 4 );
+
+ setTupletRatioToAllEditors( m_nTupletNumerator, m_nTupletDenominator );
+ // TODO why all the editors have the same grid members and there is no a unique variable for them to read?
+
+ m_pTupletLCD->setText( QString( "%1:%2" ).arg( m_nTupletNumerator ).arg( m_nTupletDenominator ) );
+ Preferences::get_instance()->setPatternEditorGridTupletRatio( m_nTupletNumerator, m_nTupletDenominator );
}
- else {
- nResolution = 0x1 << ( nSelected + 2 );
+ else { // plain note value symbols
+ m_nResolution = 0x1 << ( nSelected + 2 );
}
// INFOLOG( QString("idx %1 -> %2 resolution").arg( nSelected ).arg( nResolution ) );
- m_pDrumPatternEditor->setResolution( nResolution, bUseTriplets );
- m_pPianoRollEditor->setResolution( nResolution, bUseTriplets );
- m_pNoteVelocityEditor->setResolution( nResolution, bUseTriplets );
- m_pNoteLeadLagEditor->setResolution( nResolution, bUseTriplets );
- m_pNoteNoteKeyEditor->setResolution( nResolution, bUseTriplets );
- m_pNoteProbabilityEditor->setResolution( nResolution, bUseTriplets );
- m_pNotePanEditor->setResolution( nResolution, bUseTriplets );
-
- m_nCursorIncrement = ( bUseTriplets ? 4 : 3 ) * MAX_NOTES / ( nResolution * 3 );
- m_nCursorPosition = m_nCursorIncrement * ( m_nCursorPosition / m_nCursorIncrement );
-
- Preferences::get_instance()->setPatternEditorGridResolution( nResolution );
- Preferences::get_instance()->setPatternEditorUsingTriplets( bUseTriplets );
+ setResolutionToAllEditors( m_nResolution );
+ // TODO why all the editors have the same grid members and there is no a unique variable for them to read?
+ Preferences::get_instance()->setPatternEditorGridResolution( m_nResolution );
+
+ // move cursor to the nearest grid mark (since granularity changed)
+ setCursorPosition( fOldCursorTickPosition );
}
+void PatternEditorPanel::setResolutionToAllEditors( int nResolution ) {
+ // TODO should this be saved only in Preferences or Editor Panel or in all these classes?
+ m_pDrumPatternEditor->setResolution( nResolution );
+ m_pPianoRollEditor->setResolution( nResolution );
+ m_pNoteVelocityEditor->setResolution( nResolution );
+ m_pNoteLeadLagEditor->setResolution( nResolution );
+ m_pNoteNoteKeyEditor->setResolution( nResolution );
+ m_pNoteProbabilityEditor->setResolution( nResolution );
+ m_pNotePanEditor->setResolution( nResolution );
+}
+void PatternEditorPanel::setTupletRatioToAllEditors( int nTupletNum, int nTupletDen ) {
+ //TODO should these be saved only in Preferences or Editor Panel or in all these classes?
+ m_pDrumPatternEditor->setTupletRatio( nTupletNum, nTupletDen );
+ m_pPianoRollEditor->setTupletRatio( nTupletNum, nTupletDen );
+ m_pNoteVelocityEditor->setTupletRatio( nTupletNum, nTupletDen );
+ m_pNoteLeadLagEditor->setTupletRatio( nTupletNum, nTupletDen );
+ m_pNoteNoteKeyEditor->setTupletRatio( nTupletNum, nTupletDen );
+ m_pNoteProbabilityEditor->setTupletRatio( nTupletNum, nTupletDen );
+ m_pNotePanEditor->setTupletRatio( nTupletNum, nTupletDen );
+}
void PatternEditorPanel::selectedPatternChangedEvent()
{
@@ -1023,6 +1068,73 @@ void PatternEditorPanel::patternSizeLCDClicked()
}
}
+void PatternEditorPanel::tupletLCDClicked()
+{
+ Preferences *pPref = Preferences::get_instance();
+ bool bIsOkPressed;
+ // want to show ratio to see the input format, even if tuplet is currently off
+ QString text = QString( "%1:%2" ).arg( m_nTupletNumerator ).arg( m_nTupletDenominator );
+
+ /*QString text = m_pTupletLCD->getText(); //TODO cancel
+ if ( text == "off" ) {
+ text = "4:4";
+ }*/
+
+ QString qtmp = QInputDialog::getText( this, "Tuplet Resolution", tr( "Enter tuplet ratio (\"4\" to set off)" ),
+ QLineEdit::Normal, text, &bIsOkPressed );
+ qtmp.replace( "/", ":" ); // allow both '/' and ':' separators
+
+ if ( bIsOkPressed ) {
+ if ( m_pTupletLCD->getText() == qtmp ) { // text unchanged
+ return;
+ }
+ double fOldCursorTickPosition = getCursorFloatPosition();
+
+ QStringList parts = qtmp.split( ':' );
+ if ( parts.size() == 1 || parts.size() == 2 ) { // must reject if parts.size > 2 or null
+ bool bOk;
+ m_nTupletNumerator = parts[0].toInt( &bOk );
+ if ( bOk && parts.size() == 2 ) { // user entered both numerator and denominator
+ m_nTupletDenominator = parts[1].toInt( &bOk );
+ if ( bOk && ( m_nTupletDenominator <= 0 || m_nTupletDenominator > MAX_NOTES ) ) { //TODO what limit?
+ QMessageBox::information( this, "Hydrogen", tr( "Denominator value rejected.\nLimits: (0, %1]"
+ ).arg(MAX_NOTES) );
+ return;
+ }
+ }
+ else { // user entered numerator only. compute the standard denominator
+ m_nTupletDenominator = 1;
+ while ( ( 2 * m_nTupletDenominator ) <= m_nTupletNumerator ) {
+ m_nTupletDenominator *= 2;
+ }
+ }
+ if ( bOk && m_nTupletNumerator > 0 ) {
+ if ( m_nTupletNumerator > 99 ) { //TODO what limit?
+ QMessageBox::information( this, "Hydrogen", tr( "Tuplet numerator too big.\nMaximum = 99" ) );
+ return;
+ }
+ else { // set tuplet numerator and denominator
+ setTupletRatioToAllEditors( m_nTupletNumerator, m_nTupletDenominator );
+
+ // move cursor to the nearest grid mark.
+ setCursorPosition( fOldCursorTickPosition );
+
+ m_pTupletLCD->setText( QString( "%1:%2" ).arg( m_nTupletNumerator ).arg( m_nTupletDenominator ) );
+ pPref->setPatternEditorGridTupletRatio( m_nTupletNumerator, m_nTupletDenominator );
+ }
+ }
+ else { // user entered invalid text
+ QMessageBox::information( this, "Hydrogen", tr( "Text rejected" ) );
+ return;
+ }
+ }
+ else { // last case: user entered more than 2 ':'
+ QMessageBox::information( this, "Hydrogen", tr( "Text rejected" ) );
+ return;
+ }
+ }
+}
+
void PatternEditorPanel::moveUpBtnClicked( Button * )
{
@@ -1157,45 +1269,71 @@ void PatternEditorPanel::updatePianorollEditor()
m_pDrumPatternEditor->updateEditor(); // force an update
}
+int PatternEditorPanel::getCursorIndexPosition()
+{
+ return m_nCursorIndexPosition;
+}
+
int PatternEditorPanel::getCursorPosition()
{
- return m_nCursorPosition;
+ return round( m_nCursorIndexPosition * granularity() );
+}
+double PatternEditorPanel::getCursorFloatPosition()
+{
+ return m_nCursorIndexPosition * granularity();
}
void PatternEditorPanel::ensureCursorVisible()
{
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
uint y = nSelectedInstrument * Preferences::get_instance()->getPatternEditorGridHeight();
- m_pEditorScrollView->ensureVisible( m_nCursorPosition * m_pPatternEditorRuler->getGridWidth(), y );
+ m_pEditorScrollView->ensureVisible(
+ round( m_nCursorIndexPosition * granularity() * m_pPatternEditorRuler->getGridWidth() ),
+ y );
+}
+
+void PatternEditorPanel::setCursorIndexPosition( int nGridIndex )
+{
+ if ( nGridIndex < 0 ) {
+ m_nCursorIndexPosition = 0;
+ } else if ( round( nGridIndex * granularity() ) >= m_pPattern->get_length() ) { // TODO check boundary
+ m_nCursorIndexPosition = m_pPattern->get_length() / granularity(); // will be floored by (int) cast
+ } else {
+ m_nCursorIndexPosition = nGridIndex;
+ }
}
-void PatternEditorPanel::setCursorPosition(int nCursorPosition)
+void PatternEditorPanel::setCursorPosition( double fColumn )
{
- if ( nCursorPosition < 0 ) {
- m_nCursorPosition = 0;
- } else if ( nCursorPosition >= m_pPattern->get_length() ) {
- m_nCursorPosition = m_pPattern->get_length() - m_nCursorIncrement;
+ if ( fColumn < 0. ) {
+ m_nCursorIndexPosition = 0;
} else {
- m_nCursorPosition = nCursorPosition;
+ m_nCursorIndexPosition = round( fColumn / granularity() ); //TODO round()? since it is used in resolutionChanged, floor() ensures to not exceed the pattern length... should floor() cause problems?
+ if ( m_pPattern != nullptr) { // avoid segm fault at start
+ if ( m_nCursorIndexPosition * granularity() >= m_pPattern->get_length() ) { // THIS CAUSES segmentation fault at start because there's no pPattern)
+ m_nCursorIndexPosition--;
+ std::cout << "here\n";
+ }
+ }
}
}
int PatternEditorPanel::moveCursorLeft( int n )
{
- m_nCursorPosition = std::max( m_nCursorPosition - m_nCursorIncrement * n,
- 0 );
+ m_nCursorIndexPosition = std::max( m_nCursorIndexPosition - n,
+ 0 );
ensureCursorVisible();
- return m_nCursorPosition;
+ return m_nCursorIndexPosition;
}
int PatternEditorPanel::moveCursorRight( int n )
{
- m_nCursorPosition = std::min( m_nCursorPosition + m_nCursorIncrement * n,
- m_pPattern->get_length() - m_nCursorIncrement );
-
+ m_nCursorIndexPosition = std::min( m_nCursorIndexPosition + n,
+ (int) ceil( m_pPattern->get_length() / granularity() - 1 ) //TODO check formula
+ );
ensureCursorVisible();
- return m_nCursorPosition;
+ return m_nCursorIndexPosition;
}
diff --git a/src/gui/src/PatternEditor/PatternEditorPanel.h b/src/gui/src/PatternEditor/PatternEditorPanel.h
index 8f7a1ede0..c40e80d68 100644
--- a/src/gui/src/PatternEditor/PatternEditorPanel.h
+++ b/src/gui/src/PatternEditor/PatternEditorPanel.h
@@ -80,14 +80,25 @@ class PatternEditorPanel : public QWidget, public EventListener, public H2Core::
//~ Implements EventListener interface
void ensureCursorVisible();
- int getCursorPosition();
- void setCursorPosition(int nCursorPosition);
+ int getCursorIndexPosition();
+ int getCursorPosition(); // TODO use this in many lines rather than the explicit expression? make inline
+ double getCursorFloatPosition(); // TODO use this in many lines rather than the explicit expression? make inline
+ void setCursorIndexPosition( int nGridIndex );
+ // used to update the cursor when changing resolution
+ void setCursorPosition( double fColumn ); //TODO rename setCursorFloatTickPosition.
int moveCursorLeft( int n = 1 );
int moveCursorRight( int n = 1 );
void selectInstrumentNotes( int nInstrument );
void updateEditors( bool bPatternOnly = false );
+ //! Granularity of grid positioning ( = distance between grid marks), in tick units
+ double granularity() const { // float for tuplets
+ return (double) MAX_NOTES * m_nTupletDenominator / ( m_nTupletNumerator * m_nResolution );
+ }
+ int getTupletNumerator(){ return m_nTupletNumerator; }
+ int getTupletDenominator(){ return m_nTupletDenominator; }
+ int getResolution(){ return m_nResolution; }
public slots:
void showDrumEditor();
@@ -100,6 +111,9 @@ class PatternEditorPanel : public QWidget, public EventListener, public H2Core::
void updatePatternSizeLCD();
void patternSizeLCDClicked();
void denominatorWarningClicked();
+ void tupletLCDClicked();
+ void setResolutionToAllEditors( int nResolution );
+ void setTupletRatioToAllEditors( int nTupletNum, int nTupletDen );
void hearNotesBtnClick(Button *ref);
@@ -125,9 +139,10 @@ class PatternEditorPanel : public QWidget, public EventListener, public H2Core::
QLabel * pSLlabel;
// Editor top
- LCDDisplay * __pattern_size_LCD;
+ LCDDisplay * __pattern_size_LCD;
Button * m_pDenominatorWarning;
LCDCombo * __resolution_combo;
+ LCDDisplay * m_pTupletLCD;
ToggleButton * __show_drum_btn;
ToggleButton * __show_piano_btn;
// ~Editor top
@@ -183,10 +198,17 @@ class PatternEditorPanel : public QWidget, public EventListener, public H2Core::
Button * resDropdownBtn;
bool m_bEnablePatternResize;
-
- // Cursor positioning
- int m_nCursorPosition;
- int m_nCursorIncrement;
+
+
+ //TODO should these 3 members be here or only in preferences? or viceversa? Or maybe pref members should be overwritten
+ //only when saving preferences, rather than being updated each time the user changes the editor panel
+ uint m_nResolution;
+ int m_nTupletNumerator;
+ int m_nTupletDenominator;
+
+ /* Cursor positioning
+ * it refers to the current grid granularity (which depends on resolution and tuplet ratio) */
+ int m_nCursorIndexPosition;
//~ Cursor
virtual void dragEnterEvent(QDragEnterEvent *event) override;
diff --git a/src/gui/src/PatternEditor/PatternEditorRuler.cpp b/src/gui/src/PatternEditor/PatternEditorRuler.cpp
index e433ec4e2..3507a5658 100644
--- a/src/gui/src/PatternEditor/PatternEditorRuler.cpp
+++ b/src/gui/src/PatternEditor/PatternEditorRuler.cpp
@@ -56,9 +56,9 @@ PatternEditorRuler::PatternEditorRuler( QWidget* parent )
m_pPattern = nullptr;
- m_nGridWidth = Preferences::get_instance()->getPatternEditorGridWidth();
+ m_fGridWidth = Preferences::get_instance()->getPatternEditorGridWidth();
- m_nRulerWidth = 20 + m_nGridWidth * ( MAX_NOTES * 4 );
+ m_nRulerWidth = 20 + m_fGridWidth * ( MAX_NOTES * 4 );
m_nRulerHeight = 25;
resize( m_nRulerWidth, m_nRulerHeight );
@@ -189,7 +189,7 @@ void PatternEditorRuler::paintEvent( QPaintEvent *ev)
// gray background for unusable section of pattern
if (m_pPattern) {
- int nXStart = 20 + m_pPattern->get_length() * m_nGridWidth;
+ int nXStart = 20 + m_pPattern->get_length() * m_fGridWidth;
if ( (m_nRulerWidth - nXStart) != 0 ) {
painter.fillRect( nXStart, 0, m_nRulerWidth - nXStart, m_nRulerHeight, QColor(170,170,170) );
}
@@ -210,7 +210,7 @@ void PatternEditorRuler::paintEvent( QPaintEvent *ev)
uint nQuarter = 48;
for ( int i = 0; i < 64 ; i++ ) {
- int nText_x = 20 + nQuarter / 4 * i * m_nGridWidth;
+ int nText_x = 20 + nQuarter / 4 * i * m_fGridWidth;
if ( ( i % 4 ) == 0 ) {
painter.setPen( textColor );
painter.drawText( nText_x - 30, 0, 60, m_nRulerHeight, Qt::AlignCenter, QString("%1").arg(i / 4 + 1) );
@@ -225,7 +225,7 @@ void PatternEditorRuler::paintEvent( QPaintEvent *ev)
// draw tickPosition
if (m_nTicks != -1) {
- uint x = (uint)( 20 + m_nTicks * m_nGridWidth - 5 - 11 / 2.0 );
+ uint x = (uint)( 20 + m_nTicks * m_fGridWidth - 5 - 11 / 2.0 );
painter.drawPixmap( QRect( x, height() / 2, 11, 8 ), m_tickPosition, QRect( 0, 0, 11, 8 ) );
}
@@ -235,13 +235,13 @@ void PatternEditorRuler::paintEvent( QPaintEvent *ev)
void PatternEditorRuler::zoomIn()
{
- if (m_nGridWidth >= 3){
- m_nGridWidth *= 2;
+ if (m_fGridWidth >= 3){
+ m_fGridWidth *= 2;
}else
{
- m_nGridWidth *= 1.5;
+ m_fGridWidth *= 1.5;
}
- m_nRulerWidth = 20 + m_nGridWidth * ( MAX_NOTES * 4 );
+ m_nRulerWidth = 20 + m_fGridWidth * ( MAX_NOTES * 4 );
resize( QSize(m_nRulerWidth, m_nRulerHeight ));
delete m_pBackground;
m_pBackground = new QPixmap( m_nRulerWidth, m_nRulerHeight );
@@ -254,14 +254,14 @@ void PatternEditorRuler::zoomIn()
void PatternEditorRuler::zoomOut()
{
- if ( m_nGridWidth > 1.5 ) {
- if (m_nGridWidth > 3){
- m_nGridWidth /= 2;
+ if ( m_fGridWidth > 1.5 ) {
+ if (m_fGridWidth > 3){
+ m_fGridWidth /= 2;
}else
{
- m_nGridWidth /= 1.5;
+ m_fGridWidth /= 1.5;
}
- m_nRulerWidth = 20 + m_nGridWidth * ( MAX_NOTES * 4 );
+ m_nRulerWidth = 20 + m_fGridWidth * ( MAX_NOTES * 4 );
resize( QSize(m_nRulerWidth, m_nRulerHeight) );
delete m_pBackground;
m_pBackground = new QPixmap( m_nRulerWidth, m_nRulerHeight );
diff --git a/src/gui/src/PatternEditor/PatternEditorRuler.h b/src/gui/src/PatternEditor/PatternEditorRuler.h
index 8e01f7ef4..02c7cd3cc 100644
--- a/src/gui/src/PatternEditor/PatternEditorRuler.h
+++ b/src/gui/src/PatternEditor/PatternEditorRuler.h
@@ -57,7 +57,7 @@ class PatternEditorRuler : public QWidget, public H2Core::Object, public EventLi
void zoomIn();
void zoomOut();
float getGridWidth() const {
- return m_nGridWidth;
+ return m_fGridWidth;
};
public slots:
@@ -66,7 +66,7 @@ class PatternEditorRuler : public QWidget, public H2Core::Object, public EventLi
private:
uint m_nRulerWidth;
uint m_nRulerHeight;
- float m_nGridWidth;
+ float m_fGridWidth;
QPixmap *m_pBackground;
QPixmap m_tickPosition;
diff --git a/src/gui/src/PatternEditor/PianoRollEditor.cpp b/src/gui/src/PatternEditor/PianoRollEditor.cpp
index 4c7ca3a50..c0908d99d 100644
--- a/src/gui/src/PatternEditor/PianoRollEditor.cpp
+++ b/src/gui/src/PatternEditor/PianoRollEditor.cpp
@@ -83,10 +83,10 @@ void PianoRollEditor::updateEditor( bool bPatternOnly )
{
// uint nEditorWidth;
if ( m_pPattern ) {
- m_nEditorWidth = m_nMargin + m_nGridWidth * m_pPattern->get_length();
+ m_nEditorWidth = m_nMargin + m_fGridWidth * m_pPattern->get_length();
}
else {
- m_nEditorWidth = m_nMargin + m_nGridWidth * MAX_NOTES;
+ m_nEditorWidth = m_nMargin + m_fGridWidth * MAX_NOTES;
}
if ( !bPatternOnly ) {
m_bNeedsBackgroundUpdate = true;
@@ -312,8 +312,8 @@ void PianoRollEditor::drawPattern()
p.setPen( pen );
p.setBrush( Qt::NoBrush );
p.setRenderHint( QPainter::Antialiasing );
- p.drawRoundedRect( QRect( pos.x() - m_nGridWidth*3, pos.y()-2,
- m_nGridWidth*6, m_nGridHeight+3 ), 4, 4 );
+ p.drawRoundedRect( QRect( pos.x() - m_fGridWidth*3, pos.y()-2,
+ m_fGridWidth*6, m_nGridHeight+3 ), 4, 4 );
}
}
@@ -324,7 +324,7 @@ void PianoRollEditor::drawNote( Note *pNote, QPainter *pPainter )
Hydrogen *pHydrogen = Hydrogen::get_instance();
InstrumentList * pInstrList = pHydrogen->getSong()->getInstrumentList();
if ( pInstrList->index( pNote->get_instrument() ) == pHydrogen->getSelectedInstrumentNumber() ) {
- QPoint pos ( m_nMargin + pNote->get_position() * m_nGridWidth,
+ QPoint pos ( m_nMargin + pNote->get_position() * m_fGridWidth,
m_nGridHeight * pitchToLine( pNote->get_notekey_pitch() ) + 1);
drawNoteSymbol( *pPainter, pos, pNote );
}
@@ -437,7 +437,7 @@ void PianoRollEditor::mouseClickEvent( QMouseEvent *ev ) {
unsigned nRealColumn = 0;
if( ev->x() > m_nMargin ) {
- nRealColumn = (ev->x() - m_nMargin) / static_cast(m_nGridWidth);
+ nRealColumn = (ev->x() - m_nMargin) / static_cast(m_fGridWidth);
}
if ( ev->modifiers() & Qt::ShiftModifier ) {
@@ -496,7 +496,7 @@ void PianoRollEditor::mouseDragStartEvent( QMouseEvent *ev )
unsigned nRealColumn = 0;
if( ev->x() > m_nMargin ) {
- nRealColumn = (ev->x() - m_nMargin) / static_cast(m_nGridWidth);
+ nRealColumn = (ev->x() - m_nMargin) / static_cast(m_fGridWidth);
}
@@ -859,7 +859,7 @@ void PianoRollEditor::mouseDragEndEvent( QMouseEvent *ev )
QPoint PianoRollEditor::cursorPosition()
{
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_nGridWidth;
+ uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
uint y = m_nGridHeight * pitchToLine( m_nCursorPitch ) + 1;
return QPoint(x, y);
}
@@ -1228,7 +1228,7 @@ void PianoRollEditor::editNotePropertiesAction( int nColumn,
void PianoRollEditor::selectionMoveEndEvent( QInputEvent *ev ) {
updateModifiers( ev );
- QPoint offset = movingGridOffset();
+ QPointF offset = movingGridOffset();
if ( offset.x() == 0 && offset.y() == 0 ) {
// Move with no effect.
return;
@@ -1309,8 +1309,8 @@ std::vector PianoRollEditor::elementsIntersecti
}
// Calculate the first and last position values that this rect will intersect with
- int x_min = (r.left() - w - m_nMargin) / m_nGridWidth;
- int x_max = (r.right() + w - m_nMargin) / m_nGridWidth;
+ int x_min = (r.left() - w - m_nMargin) / m_fGridWidth;
+ int x_max = (r.right() + w - m_nMargin) / m_fGridWidth;
const Pattern::notes_t* pNotes = m_pPattern->get_notes();
std::vector result;
@@ -1318,7 +1318,7 @@ std::vector PianoRollEditor::elementsIntersecti
for ( auto it = pNotes->lower_bound( x_min ); it != pNotes->end() && it->first <= x_max; ++it ) {
Note *pNote = it->second;
if ( pNote->get_instrument() == pInstr ) {
- uint start_x = m_nMargin + pNote->get_position() * m_nGridWidth;
+ uint start_x = m_nMargin + pNote->get_position() * m_fGridWidth;
uint start_y = m_nGridHeight * pitchToLine( pNote->get_notekey_pitch() ) + 1;
if ( r.intersects( QRect( start_x -4 , start_y, w, h ) ) ) {
@@ -1335,6 +1335,6 @@ std::vector PianoRollEditor::elementsIntersecti
///
QRect PianoRollEditor::getKeyboardCursorRect() {
QPoint pos = cursorPosition();
- return QRect( pos.x() - m_nGridWidth*3, pos.y()-2,
- m_nGridWidth*6, m_nGridHeight+3 );
+ return QRect( pos.x() - m_fGridWidth*3, pos.y()-2,
+ m_fGridWidth*6, m_nGridHeight+3 );
}
diff --git a/src/gui/src/SongEditor/SongEditor.cpp b/src/gui/src/SongEditor/SongEditor.cpp
index 20ee3f063..a5e1834ba 100644
--- a/src/gui/src/SongEditor/SongEditor.cpp
+++ b/src/gui/src/SongEditor/SongEditor.cpp
@@ -1002,7 +1002,7 @@ QPoint SongEditor::movingGridOffset( ) const {
if ( rawOffset.x() < 0 ) {
x_bias = -x_bias;
}
- int x_off = (rawOffset.x() + x_bias) / (int)m_nGridWidth;
+ int x_off = (rawOffset.x() + x_bias) / (int)m_nGridWidth; // in ticks
int y_off = (rawOffset.y() + y_bias) / (int)m_nGridHeight;
return QPoint( x_off, y_off );
}
diff --git a/src/gui/src/UndoActions.h b/src/gui/src/UndoActions.h
index 0b1bd3d1b..b89d8bbc3 100644
--- a/src/gui/src/UndoActions.h
+++ b/src/gui/src/UndoActions.h
@@ -487,7 +487,7 @@ class SE_editTagAction : public QUndoCommand
class SE_addOrDeleteNoteAction : public QUndoCommand
{
public:
- SE_addOrDeleteNoteAction( int nColumn,
+ SE_addOrDeleteNoteAction( double fColumn,
int nRow,
int selectedPatternNumber,
int oldLength,
@@ -505,11 +505,11 @@ class SE_addOrDeleteNoteAction : public QUndoCommand
bool isNoteOff ){
if( isDelete ){
- setText( QObject::tr( "Delete note ( %1, %2)" ).arg( nColumn ).arg( nRow ) );
+ setText( QObject::tr( "Delete note ( %1, %2)" ).arg( fColumn ).arg( nRow ) );
} else {
- setText( QObject::tr( "Add note ( %1, %2)" ).arg( nColumn ).arg( nRow ) );
+ setText( QObject::tr( "Add note ( %1, %2)" ).arg( fColumn ).arg( nRow ) );
}
- __nColumn = nColumn;
+ __fColumn = fColumn;
__nRow = nRow;
__selectedPatternNumber = selectedPatternNumber;
__oldLength = oldLength;
@@ -531,7 +531,7 @@ class SE_addOrDeleteNoteAction : public QUndoCommand
//qDebug() << "Add note Undo ";
HydrogenApp* h2app = HydrogenApp::get_instance();
__isMidi = false; // undo is never a midi event.
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __nColumn,
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __fColumn,
__nRow,
__selectedPatternNumber,
__oldLength,
@@ -552,7 +552,7 @@ class SE_addOrDeleteNoteAction : public QUndoCommand
{
//qDebug() << "Add Note Redo " ;
HydrogenApp* h2app = HydrogenApp::get_instance();
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __nColumn,
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __fColumn,
__nRow,
__selectedPatternNumber,
__oldLength,
@@ -570,7 +570,7 @@ class SE_addOrDeleteNoteAction : public QUndoCommand
__isDelete );
}
private:
- int __nColumn;
+ double __fColumn;
int __nRow;
int __selectedPatternNumber;
int __oldLength;
@@ -630,9 +630,9 @@ class SE_deselectAndOverwriteNotesAction : public QUndoCommand
class SE_addNoteOffAction : public QUndoCommand
{
public:
- SE_addNoteOffAction( int nColumn, int nRow, int selectedPatternNumber, bool isDelete ){
- setText( QObject::tr( "Add NOTE_OFF note ( %1, %2 )" ).arg( nColumn ).arg( nRow ) );
- __nColumn = nColumn;
+ SE_addNoteOffAction( double fColumn, int nRow, int selectedPatternNumber, bool isDelete ){
+ setText( QObject::tr( "Add NOTE_OFF note ( %1, %2 )" ).arg( fColumn ).arg( nRow ) );
+ __fColumn = fColumn;
__nRow = nRow;
__selectedPatternNumber = selectedPatternNumber;
__isDelete = isDelete;
@@ -640,15 +640,45 @@ class SE_addNoteOffAction : public QUndoCommand
virtual void undo()
{
HydrogenApp* h2app = HydrogenApp::get_instance();
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __nColumn, __nRow, __selectedPatternNumber, -1, 0.8f, 0.5f, 0.5f, 0.0, 0, 0, 1.0f, false, false, false, true, !__isDelete ) ;
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __fColumn,
+ __nRow,
+ __selectedPatternNumber,
+ -1,
+ 0.8f,
+ 0.5f,
+ 0.5f,
+ 0.0,
+ 0, //TODO 0 4 ?! divbase...
+ 4,
+ 1.0f,
+ false,
+ false,
+ false,
+ true,
+ !__isDelete ) ;
}
virtual void redo()
{
HydrogenApp* h2app = HydrogenApp::get_instance();
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __nColumn, __nRow, __selectedPatternNumber, -1, 0.8f, 0.5f, 0.5f, 0.0, 0, 0, 1.0f, false, false, false, true, __isDelete );
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->addOrDeleteNoteAction( __fColumn,
+ __nRow,
+ __selectedPatternNumber,
+ -1,
+ 0.8f,
+ 0.5f,
+ 0.5f,
+ 0.0,
+ 0, //TODO 0 4 ?! divbase...
+ 4,
+ 1.0f,
+ false,
+ false,
+ false,
+ true,
+ __isDelete );
}
private:
- int __nColumn;
+ float __fColumn;
int __nRow;
int __selectedPatternNumber;
bool __isDelete;
@@ -657,13 +687,13 @@ class SE_addNoteOffAction : public QUndoCommand
class SE_moveNoteAction : public QUndoCommand
{
public:
- SE_moveNoteAction( int nOldPosition, int nOldInstrument, int nPattern, int nNewPosition, int nNewInstrument,
- H2Core::Note *pNote )
+ SE_moveNoteAction( double fOldPosition, int nOldInstrument, int nPattern, double fNewPosition,
+ int nNewInstrument, H2Core::Note *pNote )
{
- m_nOldPosition = nOldPosition;
+ m_fOldPosition = fOldPosition;
m_nOldInstrument = nOldInstrument;
m_nPattern = nPattern;
- m_nNewPosition = nNewPosition;
+ m_fNewPosition = fNewPosition;
m_nNewInstrument = nNewInstrument;
m_pNote = new H2Core::Note( pNote );
}
@@ -676,22 +706,22 @@ class SE_moveNoteAction : public QUndoCommand
virtual void undo()
{
HydrogenApp::get_instance()->getPatternEditorPanel()->getDrumPatternEditor()
- ->moveNoteAction( m_nNewPosition, m_nNewInstrument, m_nPattern,
- m_nOldPosition, m_nOldInstrument, m_pNote );
+ ->moveNoteAction( m_fNewPosition, m_nNewInstrument, m_nPattern,
+ m_fOldPosition, m_nOldInstrument, m_pNote );
}
virtual void redo()
{
HydrogenApp::get_instance()->getPatternEditorPanel()->getDrumPatternEditor()
- ->moveNoteAction( m_nOldPosition, m_nOldInstrument, m_nPattern,
- m_nNewPosition, m_nNewInstrument, m_pNote );
+ ->moveNoteAction( m_fOldPosition, m_nOldInstrument, m_nPattern,
+ m_fNewPosition, m_nNewInstrument, m_pNote );
}
private:
- int m_nOldPosition;
+ double m_fOldPosition;
int m_nOldInstrument;
int m_nPattern;
- int m_nNewPosition;
+ double m_fNewPosition;
int m_nNewInstrument;
H2Core::Note *m_pNote;
};
@@ -699,9 +729,9 @@ class SE_moveNoteAction : public QUndoCommand
class SE_editNoteLenghtAction : public QUndoCommand
{
public:
- SE_editNoteLenghtAction( int nColumn, int nRealColumn, int row, int length, int oldLength, int selectedPatternNumber ){
+ SE_editNoteLenghtAction( double fColumn, int nRealColumn, int row, double length, double oldLength, int selectedPatternNumber ){
setText( QObject::tr( "Change note length" ) );
- __nColumn = nColumn;
+ __fColumn = fColumn;
__nRealColumn = nRealColumn;
__row = row;
__length = length;
@@ -712,20 +742,20 @@ class SE_editNoteLenghtAction : public QUndoCommand
{
//qDebug() << "Change note length Undo ";
HydrogenApp* h2app = HydrogenApp::get_instance();
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->editNoteLengthAction( __nColumn, __nRealColumn, __row, __oldLength, __selectedPatternNumber );
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->editNoteLengthAction( __fColumn, __nRealColumn, __row, __oldLength, __selectedPatternNumber );
}
virtual void redo()
{
//qDebug() << "Change note length Redo " ;
HydrogenApp* h2app = HydrogenApp::get_instance();
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->editNoteLengthAction( __nColumn, __nRealColumn, __row, __length, __selectedPatternNumber );
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->editNoteLengthAction( __fColumn, __nRealColumn, __row, __length, __selectedPatternNumber );
}
private:
- int __nColumn;
+ double __fColumn;
int __nRealColumn;
int __row;
- int __oldLength;
- int __length;
+ double __oldLength;
+ double __length;
int __selectedPatternNumber;
};
@@ -830,9 +860,11 @@ class SE_pasteNotesPatternEditorAction : public QUndoCommand
class SE_fillNotesRightClickAction : public QUndoCommand
{
public:
- SE_fillNotesRightClickAction( QStringList notePositions, int nSelectedInstrument, int selectedPatternNumber ){
+ //SE_fillNotesRightClickAction( QStringList notePositions, int nSelectedInstrument, int selectedPatternNumber ){
+ SE_fillNotesRightClickAction( std::vector notePositions, int nSelectedInstrument, int selectedPatternNumber ){
+
setText( QObject::tr( "Fill notes" ) );
- __notePositions = notePositions;
+ m_notePositions = notePositions;
__nSelectedInstrument= nSelectedInstrument;
__selectedPatternNumber = selectedPatternNumber;
}
@@ -840,16 +872,18 @@ class SE_fillNotesRightClickAction : public QUndoCommand
{
//qDebug() << "fill notes Undo ";
HydrogenApp* h2app = HydrogenApp::get_instance();
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->functionFillNotesUndoAction( __notePositions, __nSelectedInstrument, __selectedPatternNumber );
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->functionFillNotesUndoAction( m_notePositions,
+ __nSelectedInstrument, __selectedPatternNumber );
}
virtual void redo()
{
//qDebug() << "fill notes Redo " ;
HydrogenApp* h2app = HydrogenApp::get_instance();
- h2app->getPatternEditorPanel()->getDrumPatternEditor()->functionFillNotesRedoAction( __notePositions, __nSelectedInstrument, __selectedPatternNumber );
+ h2app->getPatternEditorPanel()->getDrumPatternEditor()->functionFillNotesRedoAction( m_notePositions,
+ __nSelectedInstrument, __selectedPatternNumber );
}
private:
- QStringList __notePositions;
+ std::vector m_notePositions;
int __nSelectedInstrument;
int __selectedPatternNumber;
};
@@ -1315,7 +1349,7 @@ class SE_editNotePropertiesVolumeAction : public QUndoCommand
{
public:
- SE_editNotePropertiesVolumeAction( int undoColumn,
+ SE_editNotePropertiesVolumeAction( double undoColumn,
QString mode,
int nSelectedPatternNumber,
int nSelectedInstrument,
@@ -1393,7 +1427,7 @@ class SE_editNotePropertiesVolumeAction : public QUndoCommand
private:
- int __undoColumn;
+ double __undoColumn;
QString __mode;
int __nSelectedPatternNumber;
int __nSelectedInstrument;