1
Index: lmms/src/tracks/pattern.cpp
2
===================================================================
3
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
4
+++ lmms/src/tracks/pattern.cpp 2014-04-09 16:45:36.513043688 -0500
7
+ * pattern.cpp - implementation of class pattern which holds notes
9
+ * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
10
+ * Copyright (c) 2005-2007 Danny McRae <khjklujn/at/yahoo.com>
12
+ * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
14
+ * This program is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2 of the License, or (at your option) any later version.
19
+ * This program is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * General Public License for more details.
24
+ * You should have received a copy of the GNU General Public
25
+ * License along with this program (see COPYING); if not, write to the
26
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27
+ * Boston, MA 02110-1301 USA.
31
+#include <QtXml/QDomElement>
32
+#include <QtCore/QTimer>
33
+#include <QtGui/QMenu>
34
+#include <QtGui/QMessageBox>
35
+#include <QtGui/QMouseEvent>
36
+#include <QtGui/QPainter>
37
+#include <QtGui/QProgressBar>
38
+#include <QtGui/QPushButton>
39
+#include <QtAlgorithms>
42
+#include "InstrumentTrack.h"
43
+#include "templates.h"
44
+#include "gui_templates.h"
47
+#include "PianoRoll.h"
48
+#include "TrackContainer.h"
49
+#include "rename_dialog.h"
50
+#include "SampleBuffer.h"
51
+#include "AudioSampleRecorder.h"
54
+#include "bb_track_container.h"
55
+#include "string_pair_drag.h"
56
+#include "MainWindow.h"
59
+QPixmap * patternView::s_stepBtnOn = NULL;
60
+QPixmap * patternView::s_stepBtnOverlay = NULL;
61
+QPixmap * patternView::s_stepBtnOff = NULL;
62
+QPixmap * patternView::s_stepBtnOffLight = NULL;
66
+pattern::pattern( InstrumentTrack * _instrument_track ) :
67
+ trackContentObject( _instrument_track ),
68
+ m_instrumentTrack( _instrument_track ),
69
+ m_patternType( BeatPattern ),
70
+ m_steps( MidiTime::stepsPerTact() )
72
+ setName( _instrument_track->name() );
79
+pattern::pattern( const pattern & _pat_to_copy ) :
80
+ trackContentObject( _pat_to_copy.m_instrumentTrack ),
81
+ m_instrumentTrack( _pat_to_copy.m_instrumentTrack ),
82
+ m_patternType( _pat_to_copy.m_patternType ),
83
+ m_steps( _pat_to_copy.m_steps )
85
+ for( NoteVector::ConstIterator it = _pat_to_copy.m_notes.begin();
86
+ it != _pat_to_copy.m_notes.end(); ++it )
88
+ m_notes.push_back( new note( **it ) );
97
+ for( NoteVector::Iterator it = m_notes.begin();
98
+ it != m_notes.end(); ++it )
109
+void pattern::init()
111
+ connect( engine::getSong(), SIGNAL( timeSignatureChanged( int, int ) ),
112
+ this, SLOT( changeTimeSignature() ) );
113
+ saveJournallingState( false );
117
+ changeLength( length() );
118
+ restoreJournallingState();
124
+MidiTime pattern::length() const
126
+ if( m_patternType == BeatPattern )
128
+ return beatPatternLength();
131
+ tick_t max_length = MidiTime::ticksPerTact();
133
+ for( NoteVector::ConstIterator it = m_notes.begin();
134
+ it != m_notes.end(); ++it )
136
+ if( ( *it )->length() > 0 )
138
+ max_length = qMax<tick_t>( max_length,
139
+ ( *it )->endPos() );
142
+ return MidiTime( max_length ).nextFullTact() *
143
+ MidiTime::ticksPerTact();
149
+MidiTime pattern::beatPatternLength() const
151
+ tick_t max_length = MidiTime::ticksPerTact();
153
+ for( NoteVector::ConstIterator it = m_notes.begin();
154
+ it != m_notes.end(); ++it )
156
+ if( ( *it )->length() < 0 )
158
+ max_length = qMax<tick_t>( max_length,
160
+ MidiTime::ticksPerTact() /
161
+ MidiTime::stepsPerTact() );
165
+ if( m_steps != MidiTime::stepsPerTact() )
167
+ max_length = m_steps * MidiTime::ticksPerTact() /
168
+ MidiTime::stepsPerTact() ;
171
+ return MidiTime( max_length ).nextFullTact() * MidiTime::ticksPerTact();
174
+note * pattern::addNote( const note & _new_note, const bool _quant_pos )
176
+ note * new_note = new note( _new_note );
177
+ if( _quant_pos && engine::pianoRoll() )
179
+ new_note->quantizePos( engine::pianoRoll()->quantization() );
182
+ engine::mixer()->lock();
183
+ if( m_notes.size() == 0 || m_notes.back()->pos() <= new_note->pos() )
185
+ m_notes.push_back( new_note );
189
+ // simple algorithm for inserting the note between two
190
+ // notes with smaller and greater position
191
+ // maybe it could be optimized by starting in the middle and
192
+ // going forward or backward but note-inserting isn't that
193
+ // time-critical since it is usually not done while playing...
194
+ long new_note_abs_time = new_note->pos();
195
+ NoteVector::Iterator it = m_notes.begin();
197
+ while( it != m_notes.end() &&
198
+ ( *it )->pos() < new_note_abs_time )
203
+ m_notes.insert( it, new_note );
205
+ engine::mixer()->unlock();
208
+ changeLength( length() );
210
+ emit dataChanged();
220
+void pattern::removeNote( const note * _note_to_del )
222
+ engine::mixer()->lock();
223
+ NoteVector::Iterator it = m_notes.begin();
224
+ while( it != m_notes.end() )
226
+ if( *it == _note_to_del )
229
+ m_notes.erase( it );
234
+ engine::mixer()->unlock();
237
+ changeLength( length() );
239
+ emit dataChanged();
245
+// returns a pointer to the note at specified step, or NULL if note doesn't exist
247
+note * pattern::noteAtStep( int _step )
249
+ for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end();
252
+ if( ( *it )->pos() == ( _step * MidiTime::ticksPerTact() ) / MidiTime::stepsPerTact() )
261
+note * pattern::rearrangeNote( const note * _note_to_proc,
262
+ const bool _quant_pos )
264
+ // just rearrange the position of the note by removing it and adding
265
+ // a copy of it -> addNote inserts it at the correct position
266
+ note copy_of_note( *_note_to_proc );
267
+ removeNote( _note_to_proc );
269
+ return addNote( copy_of_note, _quant_pos );
274
+void pattern::rearrangeAllNotes()
276
+ // sort notes by start time
277
+ qSort(m_notes.begin(), m_notes.end(), note::lessThan );
282
+void pattern::clearNotes()
284
+ engine::mixer()->lock();
285
+ for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end();
291
+ engine::mixer()->unlock();
294
+ emit dataChanged();
300
+void pattern::setStep( int _step, bool _enabled )
302
+ for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end();
305
+ if( ( *it )->pos() == ( _step * MidiTime::ticksPerTact() ) / MidiTime::stepsPerTact() &&
306
+ ( *it )->length() <= 0 )
308
+ ( *it )->setLength( _enabled ?
309
+ -DefaultTicksPerTact : 0 );
317
+void pattern::setType( PatternTypes _new_pattern_type )
319
+ if( _new_pattern_type == BeatPattern ||
320
+ _new_pattern_type == MelodyPattern )
322
+ m_patternType = _new_pattern_type;
329
+void pattern::checkType()
331
+ NoteVector::Iterator it = m_notes.begin();
332
+ while( it != m_notes.end() )
334
+ if( ( *it )->length() > 0 )
336
+ setType( pattern::MelodyPattern );
341
+ setType( pattern::BeatPattern );
347
+void pattern::saveSettings( QDomDocument & _doc, QDomElement & _this )
349
+ _this.setAttribute( "type", m_patternType );
350
+ _this.setAttribute( "name", name() );
351
+ // as the target of copied/dragged pattern is always an existing
352
+ // pattern, we must not store actual position, instead we store -1
353
+ // which tells loadSettings() not to mess around with position
354
+ if( _this.parentNode().nodeName() == "clipboard" ||
355
+ _this.parentNode().nodeName() == "dnddata" )
357
+ _this.setAttribute( "pos", -1 );
361
+ _this.setAttribute( "pos", startPosition() );
363
+ _this.setAttribute( "len", length() );
364
+ _this.setAttribute( "muted", isMuted() );
365
+ _this.setAttribute( "steps", m_steps );
367
+ // now save settings of all notes
368
+ for( NoteVector::Iterator it = m_notes.begin();
369
+ it != m_notes.end(); ++it )
371
+ if( ( *it )->length() )
373
+ ( *it )->saveState( _doc, _this );
381
+void pattern::loadSettings( const QDomElement & _this )
383
+ m_patternType = static_cast<PatternTypes>( _this.attribute( "type"
385
+ setName( _this.attribute( "name" ) );
386
+ if( _this.attribute( "pos" ).toInt() >= 0 )
388
+ movePosition( _this.attribute( "pos" ).toInt() );
390
+ changeLength( MidiTime( _this.attribute( "len" ).toInt() ) );
391
+ if( _this.attribute( "muted" ).toInt() != isMuted() )
398
+ QDomNode node = _this.firstChild();
399
+ while( !node.isNull() )
401
+ if( node.isElement() &&
402
+ !node.toElement().attribute( "metadata" ).toInt() )
404
+ note * n = new note;
405
+ n->restoreState( node.toElement() );
406
+ m_notes.push_back( n );
408
+ node = node.nextSibling();
411
+ m_steps = _this.attribute( "steps" ).toInt();
414
+ m_steps = MidiTime::stepsPerTact();
420
+ emit dataChanged();
428
+void pattern::clear()
437
+void pattern::addSteps()
439
+ m_steps += MidiTime::stepsPerTact();
441
+ emit dataChanged();
448
+void pattern::removeSteps()
450
+ int _n = MidiTime::stepsPerTact();
453
+ for( int i = m_steps - _n; i < m_steps; ++i )
455
+ for( NoteVector::Iterator it = m_notes.begin();
456
+ it != m_notes.end(); ++it )
458
+ if( ( *it )->pos() ==
459
+ ( i * MidiTime::ticksPerTact() ) /
460
+ MidiTime::stepsPerTact() &&
461
+ ( *it )->length() <= 0 )
469
+ emit dataChanged();
477
+trackContentObjectView * pattern::createView( trackView * _tv )
479
+ return new patternView( this, _tv );
486
+void pattern::ensureBeatNotes()
488
+ // make sure, that all step-note exist
489
+ for( int i = 0; i < m_steps; ++i )
491
+ bool found = false;
492
+ for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end(); ++it )
494
+ // if a note in this position is the one we want
495
+ if( ( *it )->pos() ==
496
+ ( i * MidiTime::ticksPerTact() ) / MidiTime::stepsPerTact()
497
+ && ( *it )->length() <= 0 )
503
+ if( found == false )
505
+ addNote( note( MidiTime( 0 ), MidiTime( ( i *
506
+ MidiTime::ticksPerTact() ) /
507
+ MidiTime::stepsPerTact() ) ), false );
511
+ // remove notes we no longer need:
512
+ // that is, disabled notes that no longer fall to the steps of the new time sig
514
+ for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end(); )
516
+ bool needed = false;
517
+ for( int i = 0; i < m_steps; ++i )
519
+ if( ( *it )->pos() == ( i * MidiTime::ticksPerTact() ) / MidiTime::stepsPerTact()
520
+ || ( *it )->length() != 0 )
526
+ if( needed == false )
529
+ it = m_notes.erase( it );
538
+void pattern::updateBBTrack()
540
+ if( getTrack()->trackContainer() == engine::getBBTrackContainer() )
542
+ engine::getBBTrackContainer()->updateBBTrack( this );
545
+ if( engine::pianoRoll() && engine::pianoRoll()->currentPattern() == this )
547
+ engine::pianoRoll()->update();
554
+bool pattern::empty()
556
+ for( NoteVector::ConstIterator it = m_notes.begin();
557
+ it != m_notes.end(); ++it )
559
+ if( ( *it )->length() != 0 )
570
+void pattern::changeTimeSignature()
572
+ MidiTime last_pos = MidiTime::ticksPerTact();
573
+ for( NoteVector::ConstIterator cit = m_notes.begin();
574
+ cit != m_notes.end(); ++cit )
576
+ if( ( *cit )->length() < 0 && ( *cit )->pos() > last_pos )
578
+ last_pos = ( *cit )->pos()+MidiTime::ticksPerTact() /
579
+ MidiTime::stepsPerTact();
582
+ last_pos = last_pos.nextFullTact() * MidiTime::ticksPerTact();
583
+ for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end(); )
585
+ if( ( *it )->length() == 0 && ( *it )->pos() >= last_pos )
588
+ it = m_notes.erase( it );
596
+ m_steps = qMax<tick_t>(
597
+ qMax<tick_t>( m_steps, MidiTime::stepsPerTact() ),
598
+ last_pos.getTact() * MidiTime::stepsPerTact() );
607
+patternView::patternView( pattern * _pattern, trackView * _parent ) :
608
+ trackContentObjectView( _pattern, _parent ),
611
+ m_needsUpdate( true )
613
+ connect( engine::pianoRoll(), SIGNAL( currentPatternChanged() ),
614
+ this, SLOT( update() ) );
616
+ if( s_stepBtnOn == NULL )
618
+ s_stepBtnOn = new QPixmap( embed::getIconPixmap(
619
+ "step_btn_on_100" ) );
622
+ if( s_stepBtnOverlay == NULL )
624
+ s_stepBtnOverlay = new QPixmap( embed::getIconPixmap(
625
+ "step_btn_on_yellow" ) );
628
+ if( s_stepBtnOff == NULL )
630
+ s_stepBtnOff = new QPixmap( embed::getIconPixmap(
631
+ "step_btn_off" ) );
634
+ if( s_stepBtnOffLight == NULL )
636
+ s_stepBtnOffLight = new QPixmap( embed::getIconPixmap(
637
+ "step_btn_off_light" ) );
640
+ setFixedHeight( parentWidget()->height() - 2 );
641
+ setAutoResizeEnabled( false );
643
+ toolTip::add( this,
644
+ tr( "double-click to open this pattern in piano-roll\n"
645
+ "use mouse wheel to set volume of a step" ) );
646
+ setStyle( QApplication::style() );
654
+patternView::~patternView()
656
+ if( engine::pianoRoll()->currentPattern() == m_pat )
658
+ engine::pianoRoll()->setCurrentPattern( NULL );
659
+ // we have to have the song-editor to stop playing if it played
661
+ if( engine::getSong()->isPlaying() &&
662
+ engine::getSong()->playMode() ==
663
+ song::Mode_PlayPattern )
665
+ engine::getSong()->playPattern( NULL );
674
+void patternView::update()
676
+ m_needsUpdate = true;
677
+ m_pat->changeLength( m_pat->length() );
678
+ trackContentObjectView::update();
684
+void patternView::openInPianoRoll()
686
+ engine::pianoRoll()->setCurrentPattern( m_pat );
687
+ engine::pianoRoll()->parentWidget()->show();
688
+ engine::pianoRoll()->setFocus();
694
+void patternView::resetName()
696
+ m_pat->setName( m_pat->m_instrumentTrack->name() );
702
+void patternView::changeName()
704
+ QString s = m_pat->name();
705
+ renameDialog rename_dlg( s );
707
+ m_pat->setName( s );
713
+void patternView::constructContextMenu( QMenu * _cm )
715
+ QAction * a = new QAction( embed::getIconPixmap( "piano" ),
716
+ tr( "Open in piano-roll" ), _cm );
717
+ _cm->insertAction( _cm->actions()[0], a );
718
+ connect( a, SIGNAL( triggered( bool ) ),
719
+ this, SLOT( openInPianoRoll() ) );
720
+ _cm->insertSeparator( _cm->actions()[1] );
722
+ _cm->addSeparator();
724
+ _cm->addAction( embed::getIconPixmap( "edit_erase" ),
725
+ tr( "Clear all notes" ), m_pat, SLOT( clear() ) );
726
+ _cm->addSeparator();
728
+ _cm->addAction( embed::getIconPixmap( "reload" ), tr( "Reset name" ),
729
+ this, SLOT( resetName() ) );
730
+ _cm->addAction( embed::getIconPixmap( "edit_rename" ),
731
+ tr( "Change name" ),
732
+ this, SLOT( changeName() ) );
733
+ _cm->addSeparator();
735
+ _cm->addAction( embed::getIconPixmap( "step_btn_add" ),
736
+ tr( "Add steps" ), m_pat, SLOT( addSteps() ) );
737
+ _cm->addAction( embed::getIconPixmap( "step_btn_remove" ),
738
+ tr( "Remove steps" ), m_pat, SLOT( removeSteps() ) );
744
+void patternView::mouseDoubleClickEvent( QMouseEvent * _me )
746
+ if( _me->button() != Qt::LeftButton )
751
+ if( m_pat->type() == pattern::MelodyPattern ||
752
+ !( m_pat->type() == pattern::BeatPattern &&
753
+ ( pixelsPerTact() >= 192 ||
754
+ m_pat->m_steps != MidiTime::stepsPerTact() ) &&
755
+ _me->y() > height() - s_stepBtnOff->height() ) )
764
+void patternView::mousePressEvent( QMouseEvent * _me )
766
+ if( _me->button() == Qt::LeftButton &&
767
+ m_pat->m_patternType == pattern::BeatPattern &&
768
+ ( fixedTCOs() || pixelsPerTact() >= 96 ||
769
+ m_pat->m_steps != MidiTime::stepsPerTact() ) &&
770
+ _me->y() > height() - s_stepBtnOff->height() )
772
+ // when mouse button is pressed in beat/bassline -mode
775
+// get the step number that was clicked on and
776
+// do calculations in floats to prevent rounding errors...
777
+ float tmp = ( ( float(_me->x()) - TCO_BORDER_WIDTH ) *
778
+ float( m_pat -> m_steps ) ) / float(width() - TCO_BORDER_WIDTH*2);
780
+ int step = int( tmp );
782
+// debugging to ensure we get the correct step...
783
+// qDebug( "Step (%f) %d", tmp, step );
785
+ if( step >= m_pat->m_steps )
787
+ qDebug( "Something went wrong in pattern.cpp: step %d doesn't exist in pattern!", step );
791
+ note * n = m_pat->noteAtStep( step );
793
+ // if note at step not found, ensureBeatNotes and try again
796
+ m_pat -> ensureBeatNotes();
797
+ n = m_pat->noteAtStep( step );
798
+ if( n == NULL ) // still can't find a note? bail!
800
+ qDebug( "Something went wrong in pattern.cpp: couldn't add note at step %d!", step );
804
+ else // note at step found
806
+ if( n->length() < 0 )
808
+ n->setLength( 0 ); // set note as enabled beat note
812
+ n->setLength( -DefaultTicksPerTact ); // set note as disabled beat note
816
+ engine::getSong()->setModified();
819
+ if( engine::pianoRoll()->currentPattern() == m_pat )
821
+ engine::pianoRoll()->update();
826
+ // if not in beat/bassline -mode, let parent class handle the event
829
+ trackContentObjectView::mousePressEvent( _me );
836
+void patternView::wheelEvent( QWheelEvent * _we )
838
+ if( m_pat->m_patternType == pattern::BeatPattern &&
839
+ ( fixedTCOs() || pixelsPerTact() >= 96 ||
840
+ m_pat->m_steps != MidiTime::stepsPerTact() ) &&
841
+ _we->y() > height() - s_stepBtnOff->height() )
843
+// get the step number that was wheeled on and
844
+// do calculations in floats to prevent rounding errors...
845
+ float tmp = ( ( float(_we->x()) - TCO_BORDER_WIDTH ) *
846
+ float( m_pat -> m_steps ) ) / float(width() - TCO_BORDER_WIDTH*2);
848
+ int step = int( tmp );
850
+ if( step >= m_pat->m_steps )
858
+ note * n = m_pat->noteAtStep( step );
861
+ vol = n->getVolume();
865
+ if( len == 0 && _we->delta() > 0 )
867
+ n->setLength( -DefaultTicksPerTact );
870
+ else if( _we->delta() > 0 )
872
+ n->setVolume( qMin( 100, vol + 5 ) );
876
+ n->setVolume( qMax( 0, vol - 5 ) );
879
+ engine::getSong()->setModified();
881
+ if( engine::pianoRoll()->currentPattern() == m_pat )
883
+ engine::pianoRoll()->update();
889
+ trackContentObjectView::wheelEvent( _we );
896
+void patternView::paintEvent( QPaintEvent * )
898
+ if( m_needsUpdate == false )
900
+ QPainter p( this );
901
+ p.drawPixmap( 0, 0, m_paintPixmap );
905
+ QPainter _p( this );
906
+ const QColor styleColor = _p.pen().brush().color();
908
+ m_pat->changeLength( m_pat->length() );
910
+ m_needsUpdate = false;
912
+ if( m_paintPixmap.isNull() == true || m_paintPixmap.size() != size() )
914
+ m_paintPixmap = QPixmap( size() );
917
+ QPainter p( &m_paintPixmap );
919
+ QLinearGradient lingrad( 0, 0, 0, height() );
923
+ if(( m_pat->m_patternType != pattern::BeatPattern ) &&
924
+ !( m_pat->getTrack()->isMuted() || m_pat->isMuted() ))
925
+ c = isSelected() ? QColor( 0, 0, 224 )
928
+ c = QColor( 80, 80, 80 );
930
+ if( m_pat->m_patternType != pattern::BeatPattern )
932
+ lingrad.setColorAt( 1, c.darker( 300 ) );
933
+ lingrad.setColorAt( 0, c );
937
+ lingrad.setColorAt( 0, c.darker( 300 ) );
938
+ lingrad.setColorAt( 1, c );
941
+ p.setBrush( lingrad );
942
+ if( engine::pianoRoll()->currentPattern() == m_pat && m_pat->m_patternType != pattern::BeatPattern )
943
+ p.setPen( c.lighter( 130 ) );
945
+ p.setPen( c.darker( 300 ) );
946
+ p.drawRect( QRect( 0, 0, width() - 1, height() - 1 ) );
948
+ p.setBrush( QBrush() );
949
+ if( m_pat->m_patternType != pattern::BeatPattern )
951
+ if( engine::pianoRoll()->currentPattern() == m_pat )
952
+ p.setPen( c.lighter( 160 ) );
954
+ p.setPen( c.lighter( 130 ) );
955
+ p.drawRect( QRect( 1, 1, width() - 3, height() - 3 ) );
958
+ const float ppt = fixedTCOs() ?
959
+ ( parentWidget()->width() - 2 * TCO_BORDER_WIDTH )
960
+ / (float) m_pat->length().getTact() :
963
+ const int x_base = TCO_BORDER_WIDTH;
964
+ p.setPen( c.darker( 300 ) );
966
+ for( tact_t t = 1; t < m_pat->length().getTact(); ++t )
968
+ p.drawLine( x_base + static_cast<int>( ppt * t ) - 1,
969
+ TCO_BORDER_WIDTH, x_base + static_cast<int>(
970
+ ppt * t ) - 1, 5 );
971
+ p.drawLine( x_base + static_cast<int>( ppt * t ) - 1,
972
+ height() - ( 4 + 2 * TCO_BORDER_WIDTH ),
973
+ x_base + static_cast<int>( ppt * t ) - 1,
974
+ height() - 2 * TCO_BORDER_WIDTH );
977
+// melody pattern paint event
979
+ if( m_pat->m_patternType == pattern::MelodyPattern )
981
+ if( m_pat->m_notes.size() > 0 )
983
+ // first determine the central tone so that we can
984
+ // display the area where most of the m_notes are
985
+ // also calculate min/max tones so the tonal range can be
986
+ // properly stretched accross the pattern vertically
988
+ int central_key = 0;
990
+ int min_key = 9999999;
991
+ int total_notes = 0;
993
+ for( NoteVector::Iterator it = m_pat->m_notes.begin();
994
+ it != m_pat->m_notes.end(); ++it )
996
+ if( ( *it )->length() > 0 )
998
+ max_key = qMax( max_key, ( *it )->key() );
999
+ min_key = qMin( min_key, ( *it )->key() );
1000
+ central_key += ( *it )->key();
1005
+ if( total_notes > 0 )
1007
+ central_key = central_key / total_notes;
1008
+ const int keyrange = qMax( qMax( max_key - central_key, central_key - min_key ), 1 );
1011
+ // qDebug( "keyrange: %d", keyrange );
1013
+ // determine height of the pattern view, sans borders
1014
+ const int ht = height() - 1 - TCO_BORDER_WIDTH * 2;
1016
+ // determine maximum height value for drawing bounds checking
1017
+ const int max_ht = height() - 1 - TCO_BORDER_WIDTH;
1019
+ // set colour based on mute status
1020
+ if( m_pat->getTrack()->isMuted() ||
1021
+ m_pat->isMuted() )
1023
+ p.setPen( QColor( 160, 160, 160 ) );
1027
+ p.setPen( QColor( 255, 255, 255 ) ); /// \todo make this a qproperty
1030
+ // scan through all the notes and draw them on the pattern
1031
+ for( NoteVector::Iterator it =
1032
+ m_pat->m_notes.begin();
1033
+ it != m_pat->m_notes.end(); ++it )
1035
+ // calculate relative y-position
1036
+ const float y_key =
1037
+ ( float( central_key - ( *it )->key() ) / keyrange + 1.0f ) / 2;
1038
+ // multiply that by pattern height
1039
+ const int y_pos = static_cast<int>( TCO_BORDER_WIDTH + y_key * ht );
1042
+ // if( ( *it )->length() > 0 ) qDebug( "key %d, central_key %d, y_key %f, y_pos %d", ( *it )->key(), central_key, y_key, y_pos );
1044
+ // check that note isn't out of bounds, and has a length
1045
+ if( ( *it )->length() > 0 &&
1046
+ y_pos >= TCO_BORDER_WIDTH &&
1049
+ // calculate start and end x-coords of the line to be drawn
1050
+ const int x1 = x_base +
1052
+ ( ( *it )->pos() * ( ppt / MidiTime::ticksPerTact() ) );
1053
+ const int x2 = x_base +
1055
+ ( ( ( *it )->pos() + ( *it )->length() ) * ( ppt / MidiTime::ticksPerTact() ) );
1057
+ // check bounds, draw line
1058
+ if( x1 < width() - TCO_BORDER_WIDTH )
1059
+ p.drawLine( x1, y_pos,
1060
+ qMin( x2, width() - TCO_BORDER_WIDTH ), y_pos );
1067
+// beat pattern paint event
1069
+ else if( m_pat->m_patternType == pattern::BeatPattern &&
1070
+ ( fixedTCOs() || ppt >= 96
1071
+ || m_pat->m_steps != MidiTime::stepsPerTact() ) )
1074
+ QPixmap stepoverlay;
1077
+ const int steps = qMax( 1,
1079
+ const int w = width() - 2 * TCO_BORDER_WIDTH;
1081
+ // scale step graphics to fit the beat pattern length
1082
+ stepon = s_stepBtnOn->scaled( w / steps,
1083
+ s_stepBtnOn->height(),
1084
+ Qt::IgnoreAspectRatio,
1085
+ Qt::SmoothTransformation );
1086
+ stepoverlay = s_stepBtnOverlay->scaled( w / steps,
1087
+ s_stepBtnOn->height(),
1088
+ Qt::IgnoreAspectRatio,
1089
+ Qt::SmoothTransformation );
1090
+ stepoff = s_stepBtnOff->scaled( w / steps,
1091
+ s_stepBtnOff->height(),
1092
+ Qt::IgnoreAspectRatio,
1093
+ Qt::SmoothTransformation );
1094
+ stepoffl = s_stepBtnOffLight->scaled( w / steps,
1095
+ s_stepBtnOffLight->height(),
1096
+ Qt::IgnoreAspectRatio,
1097
+ Qt::SmoothTransformation );
1099
+ for( int it = 0; it < steps; it++ ) // go through all the steps in the beat pattern
1101
+ note * n = m_pat->noteAtStep( it );
1103
+ // figure out x and y coordinates for step graphic
1104
+ const int x = TCO_BORDER_WIDTH + static_cast<int>( it * w / steps );
1105
+ const int y = height() - s_stepBtnOff->height() - 1;
1107
+ // get volume and length of note, if noteAtStep returned null
1108
+ // (meaning, note at step doesn't exist for some reason)
1109
+ // then set both at zero, ie. treat as an off step
1110
+ const int vol = ( n != NULL ? n->getVolume() : 0 );
1111
+ const int len = ( n != NULL ? int( n->length() ) : 0 );
1115
+ p.drawPixmap( x, y, stepoff );
1116
+ for( int i = 0; i < vol / 5 + 1; ++i )
1118
+ p.drawPixmap( x, y, stepon );
1120
+ for( int i = 0; i < ( 25 + ( vol - 75 ) ) / 5;
1123
+ p.drawPixmap( x, y, stepoverlay );
1126
+ else if( ( it / 4 ) % 2 )
1128
+ p.drawPixmap( x, y, stepoffl );
1132
+ p.drawPixmap( x, y, stepoff );
1137
+ p.setFont( pointSize<8>( p.font() ) );
1139
+ QColor text_color = ( m_pat->isMuted() || m_pat->getTrack()->isMuted() )
1140
+ ? QColor( 30, 30, 30 )
1141
+ : QColor( 255, 255, 255 );
1143
+ if( m_pat->name() != m_pat->instrumentTrack()->name() )
1145
+ p.setPen( QColor( 0, 0, 0 ) );
1146
+ p.drawText( 4, p.fontMetrics().height()+1, m_pat->name() );
1147
+ p.setPen( text_color );
1148
+ p.drawText( 3, p.fontMetrics().height(), m_pat->name() );
1151
+ if( m_pat->isMuted() )
1153
+ p.drawPixmap( 3, p.fontMetrics().height() + 1,
1154
+ embed::getIconPixmap( "muted", 16, 16 ) );
1159
+ _p.drawPixmap( 0, 0, m_paintPixmap );
1166
+#include "moc_pattern.cxx"