1 /*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 *
5 * http://www.hydrogen-music.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY, without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23 #include "DrumPatternEditor.h"
24 #include "PatternEditorPanel.h"
25 #include "NotePropertiesRuler.h"
26
27 #include <hydrogen/globals.h>
28 #include <hydrogen/basics/song.h>
29 #include <hydrogen/hydrogen.h>
30 #include <hydrogen/Preferences.h>
31 #include <hydrogen/event_queue.h>
32 #include <hydrogen/basics/drumkit_component.h>
33 #include <hydrogen/basics/instrument.h>
34 #include <hydrogen/basics/instrument_list.h>
35 #include <hydrogen/basics/instrument_component.h>
36 #include <hydrogen/basics/pattern.h>
37 #include <hydrogen/basics/pattern_list.h>
38 #include <hydrogen/basics/adsr.h>
39 #include <hydrogen/basics/note.h>
40 #include <hydrogen/audio_engine.h>
41
42 #include "UndoActions.h"
43 #include "../HydrogenApp.h"
44 #include "../Mixer/Mixer.h"
45 #include "../Skin.h"
46
47 #include <math.h>
48 #include <cassert>
49 #include <algorithm>
50
51 using namespace std;
52 using namespace H2Core;
53
54 const char* DrumPatternEditor::__class_name = "DrumPatternEditor";
55
DrumPatternEditor(QWidget * parent,PatternEditorPanel * panel)56 DrumPatternEditor::DrumPatternEditor(QWidget* parent, PatternEditorPanel *panel)
57 : QWidget( parent )
58 , Object( __class_name )
59 , m_nResolution( 8 )
60 , m_bUseTriplets( false )
61 , m_bRightBtnPressed( false )
62 , m_pDraggedNote( nullptr )
63 , m_pPattern( nullptr )
64 , m_pPatternEditorPanel( panel )
65 {
66 setFocusPolicy(Qt::ClickFocus);
67
68 m_nGridWidth = Preferences::get_instance()->getPatternEditorGridWidth();
69 m_nGridHeight = Preferences::get_instance()->getPatternEditorGridHeight();
70
71 unsigned nEditorWidth = 20 + m_nGridWidth * ( MAX_NOTES * 4 );
72 m_nEditorHeight = m_nGridHeight * MAX_INSTRUMENTS;
73
74 resize( nEditorWidth, m_nEditorHeight );
75
76 HydrogenApp::get_instance()->addEventListener( this );
77
78 }
79
80
81
~DrumPatternEditor()82 DrumPatternEditor::~DrumPatternEditor()
83 {
84 }
85
86
87
updateEditor()88 void DrumPatternEditor::updateEditor()
89 {
90 Hydrogen* engine = Hydrogen::get_instance();
91
92 // check engine state
93 int state = engine->getState();
94 if ( (state != STATE_READY) && (state != STATE_PLAYING) ) {
95 ERRORLOG( "FIXME: skipping pattern editor update (state should be READY or PLAYING)" );
96 return;
97 }
98
99 Hydrogen *pEngine = Hydrogen::get_instance();
100 PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
101 int nSelectedPatternNumber = pEngine->getSelectedPatternNumber();
102 if ( (nSelectedPatternNumber != -1) && ( (uint)nSelectedPatternNumber < pPatternList->size() ) ) {
103 m_pPattern = pPatternList->get( nSelectedPatternNumber );
104 }
105 else {
106 m_pPattern = nullptr;
107 }
108 __selectedPatternNumber = nSelectedPatternNumber;
109
110
111 uint nEditorWidth;
112 if ( m_pPattern ) {
113 nEditorWidth = 20 + m_nGridWidth * m_pPattern->get_length();
114 }
115 else {
116 nEditorWidth = 20 + m_nGridWidth * MAX_NOTES;
117 }
118 resize( nEditorWidth, height() );
119
120 // redraw all
121 update( 0, 0, width(), height() );
122 }
123
124
125
getColumn(QMouseEvent * ev)126 int DrumPatternEditor::getColumn(QMouseEvent *ev)
127 {
128 int nBase;
129 if (m_bUseTriplets) {
130 nBase = 3;
131 }
132 else {
133 nBase = 4;
134 }
135 float nWidth = (m_nGridWidth * 4 * MAX_NOTES) / (nBase * m_nResolution);
136
137 int x = ev->x();
138 int nColumn;
139 nColumn = x - 20 + (nWidth / 2);
140 nColumn = nColumn / nWidth;
141 nColumn = (nColumn * 4 * MAX_NOTES) / (nBase * m_nResolution);
142 return nColumn;
143 }
144
145
146
mousePressEvent(QMouseEvent * ev)147 void DrumPatternEditor::mousePressEvent(QMouseEvent *ev)
148 {
149 if ( m_pPattern == nullptr ) {
150 return;
151 }
152 Song *pSong = Hydrogen::get_instance()->getSong();
153 int nInstruments = pSong->get_instrument_list()->size();
154 int row = (int)( ev->y() / (float)m_nGridHeight);
155 if (row >= nInstruments) {
156 return;
157 }
158 int nColumn = getColumn( ev );
159 int nRealColumn = 0;
160 if( ev->x() > 20 ) {
161 nRealColumn = ev->x() / static_cast<float>(m_nGridWidth) - 20;
162 }
163 if ( nColumn >= (int)m_pPattern->get_length() ) {
164 update( 0, 0, width(), height() );
165 return;
166 }
167 Instrument *pSelectedInstrument = pSong->get_instrument_list()->get( row );
168
169
170 if( ev->button() == Qt::LeftButton && (ev->modifiers() & Qt::ShiftModifier) )
171 {
172 //shift + leftClick: add noteOff note
173 SE_addNoteRightClickAction *action = new SE_addNoteRightClickAction( nColumn, row, __selectedPatternNumber );
174 HydrogenApp::get_instance()->m_pUndoStack->push( action );
175 }
176 else if (ev->button() == Qt::LeftButton ) {
177
178 H2Core::Note *pDraggedNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument );
179
180 int oldLength = -1;
181 float oldVelocity = 0.8f;
182 float oldPan_L = 0.5f;
183 float oldPan_R = 0.5f;
184 float oldLeadLag = 0.0f;
185 Note::Key oldNoteKeyVal = Note::C;
186 Note::Octave oldOctaveKeyVal = Note::P8;
187
188 bool noteExisted = false;
189 if( pDraggedNote ){
190 oldLength = pDraggedNote->get_length();
191 oldVelocity = pDraggedNote->get_velocity();
192 oldPan_L = pDraggedNote->get_pan_l();
193 oldPan_R = pDraggedNote->get_pan_r();
194 oldLeadLag = pDraggedNote->get_lead_lag();
195 oldNoteKeyVal = pDraggedNote->get_key();
196 oldOctaveKeyVal = pDraggedNote->get_octave();
197 noteExisted = true;
198 }
199
200
201 SE_addNoteAction *action = new SE_addNoteAction( nColumn,
202 row,
203 __selectedPatternNumber,
204 oldLength,
205 oldVelocity,
206 oldPan_L,
207 oldPan_R,
208 oldLeadLag,
209 oldNoteKeyVal,
210 oldOctaveKeyVal,
211 noteExisted,
212 Preferences::get_instance()->getHearNewNotes(),
213 false,
214 false);
215
216 HydrogenApp::get_instance()->m_pUndoStack->push( action );
217
218 } else if (ev->button() == Qt::RightButton ) {
219 m_bRightBtnPressed = true;
220
221 m_pDraggedNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
222 // needed for undo note length
223 __nRealColumn = nRealColumn;
224 __nColumn = nColumn;
225 __row = row;
226 if( m_pDraggedNote ){
227 __oldLength = m_pDraggedNote->get_length();
228 } else {
229 __oldLength = -1;
230 }
231 }
232 }
233
addOrDeleteNoteAction(int nColumn,int row,int selectedPatternNumber,int oldLength,float oldVelocity,float oldPan_L,float oldPan_R,float oldLeadLag,int oldNoteKeyVal,int oldOctaveKeyVal,bool listen,bool isMidi,bool isInstrumentMode,bool isNoteOff)234 void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
235 int row,
236 int selectedPatternNumber,
237 int oldLength,
238 float oldVelocity,
239 float oldPan_L,
240 float oldPan_R,
241 float oldLeadLag,
242 int oldNoteKeyVal,
243 int oldOctaveKeyVal,
244 bool listen,
245 bool isMidi,
246 bool isInstrumentMode,
247 bool isNoteOff)
248 {
249
250 Hydrogen *pEngine = Hydrogen::get_instance();
251 PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
252 H2Core::Pattern *pPattern;
253
254 if ( ( selectedPatternNumber != -1 ) && ( (uint)selectedPatternNumber < pPatternList->size() ) ) {
255 pPattern = pPatternList->get( selectedPatternNumber );
256 }
257 else {
258 pPattern = nullptr;
259 }
260
261
262 Song *pSong = Hydrogen::get_instance()->getSong();
263
264 Instrument *pSelectedInstrument = pSong->get_instrument_list()->get( row );
265 m_bRightBtnPressed = false;
266
267 AudioEngine::get_instance()->lock( RIGHT_HERE ); // lock the audio engine
268
269 bool bNoteAlreadyExist = false;
270 if(!isInstrumentMode){
271 Pattern::notes_t* notes = (Pattern::notes_t*)pPattern->get_notes();
272 FOREACH_NOTE_IT_BOUND(notes,it,nColumn) {
273 Note *pNote = it->second;
274 assert( pNote );
275 if ( pNote->get_instrument() == pSelectedInstrument ) {
276
277 // the note exists...remove it!
278 bNoteAlreadyExist = true;
279 delete pNote;
280 notes->erase( it );
281 break;
282 }
283 }
284 }
285 else
286 {
287 Note* note = pPattern->find_note( nColumn, -1, pSelectedInstrument, (Note::Key)oldNoteKeyVal, (Note::Octave)oldOctaveKeyVal );
288 if( note ) {
289
290 // the note exists...remove it!
291 bNoteAlreadyExist = true;
292 m_pPattern->remove_note( note );
293 delete note;
294 }
295 }
296
297
298 if ( bNoteAlreadyExist == false ) {
299 // create the new note
300 unsigned nPosition = nColumn;
301 float fVelocity = oldVelocity;
302 float fPan_L = oldPan_L ;
303 float fPan_R = oldPan_R;
304 int nLength = oldLength;
305
306
307 if( isNoteOff )
308 {
309 fVelocity = 0.0f;
310 fPan_L = 0.5f;
311 fPan_R = 0.5f;
312 nLength = 1;
313 }
314
315 const float fPitch = 0.0f;
316 Note *pNote = new Note( pSelectedInstrument, nPosition, fVelocity, fPan_L, fPan_R, nLength, fPitch );
317 pNote->set_note_off( isNoteOff );
318 if( !isNoteOff ) pNote->set_lead_lag( oldLeadLag );
319 pNote->set_key_octave( (Note::Key)oldNoteKeyVal, (Note::Octave)oldOctaveKeyVal );
320 pPattern->insert_note( pNote );
321
322 if(isMidi){
323 pNote->set_just_recorded(true);
324 }
325 // hear note
326 if ( listen && !isNoteOff ) {
327 Note *pNote2 = new Note( pSelectedInstrument, 0, fVelocity, fPan_L, fPan_R, nLength, fPitch);
328 AudioEngine::get_instance()->get_sampler()->note_on(pNote2);
329 }
330 }
331 pSong->set_is_modified( true );
332 AudioEngine::get_instance()->unlock(); // unlock the audio engine
333
334 // update the selected line
335 int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
336 if (nSelectedInstrument != row) {
337 Hydrogen::get_instance()->setSelectedInstrumentNumber( row );
338 }
339 else {
340 update( 0, 0, width(), height() );
341 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
342 m_pPatternEditorPanel->getPanEditor()->updateEditor();
343 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
344 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
345 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
346 }
347 }
348
349
mouseReleaseEvent(QMouseEvent * ev)350 void DrumPatternEditor::mouseReleaseEvent(QMouseEvent *ev)
351 {
352 UNUSED( ev );
353 setCursor( QCursor( Qt::ArrowCursor ) );
354
355 if (m_pPattern == nullptr) {
356 return;
357 }
358
359 if ( m_bRightBtnPressed && m_pDraggedNote ) {
360 if ( m_pDraggedNote->get_note_off() ) return;
361
362 SE_editNoteLenghtAction *action = new SE_editNoteLenghtAction( m_pDraggedNote->get_position(), m_pDraggedNote->get_position(), __row, m_pDraggedNote->get_length(),__oldLength, __selectedPatternNumber);
363 HydrogenApp::get_instance()->m_pUndoStack->push( action );
364 }
365 }
366
367
editNoteLengthAction(int nColumn,int nRealColumn,int row,int length,int selectedPatternNumber)368 void DrumPatternEditor::editNoteLengthAction( int nColumn, int nRealColumn, int row, int length, int selectedPatternNumber )
369 {
370 Hydrogen *pEngine = Hydrogen::get_instance();
371 PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
372
373 H2Core::Pattern *pPattern;
374 if ( (selectedPatternNumber != -1) && ( (uint)selectedPatternNumber < pPatternList->size() ) ) {
375 pPattern = pPatternList->get( selectedPatternNumber );
376 } else {
377 pPattern = nullptr;
378 }
379
380 Note *pDraggedNote;
381 Song *pSong = pEngine->getSong();
382
383
384 Instrument *pSelectedInstrument = pSong->get_instrument_list()->get( row );
385
386 AudioEngine::get_instance()->lock( RIGHT_HERE );
387 pDraggedNote = pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
388 if( pDraggedNote ){
389 pDraggedNote->set_length( length );
390 }
391 AudioEngine::get_instance()->unlock();
392
393 update( 0, 0, width(), height() );
394
395 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
396 m_pPatternEditorPanel->getPanEditor()->updateEditor();
397 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
398 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
399 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
400 }
401
402
403
mouseMoveEvent(QMouseEvent * ev)404 void DrumPatternEditor::mouseMoveEvent(QMouseEvent *ev)
405 {
406 if (m_pPattern == nullptr) {
407 return;
408 }
409
410 int row = MAX_INSTRUMENTS - 1 - (ev->y() / (int)m_nGridHeight);
411 if (row >= MAX_INSTRUMENTS) {
412 return;
413 }
414
415
416 if ( m_bRightBtnPressed && m_pDraggedNote ) {
417 if ( m_pDraggedNote->get_note_off() ) return;
418 int nTickColumn = getColumn( ev );
419
420 AudioEngine::get_instance()->lock( RIGHT_HERE ); // lock the audio engine
421 int nLen = nTickColumn - (int)m_pDraggedNote->get_position();
422
423 if (nLen <= 0) {
424 nLen = -1;
425 }
426
427 float fNotePitch = m_pDraggedNote->get_octave() * 12 + m_pDraggedNote->get_key();
428 float fStep = 0;
429 if(nLen > -1){
430 fStep = pow( 1.0594630943593, ( double )fNotePitch );
431 }else
432 {
433 fStep = 1.0;
434 }
435 m_pDraggedNote->set_length( nLen * fStep);
436
437 Hydrogen::get_instance()->getSong()->set_is_modified( true );
438 AudioEngine::get_instance()->unlock(); // unlock the audio engine
439
440 //__draw_pattern();
441 update( 0, 0, width(), height() );
442 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
443 m_pPatternEditorPanel->getPanEditor()->updateEditor();
444 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
445 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
446 }
447
448 }
449
450
451
keyPressEvent(QKeyEvent * ev)452 void DrumPatternEditor::keyPressEvent (QKeyEvent *ev)
453 {
454 ev->ignore();
455 }
456
457
458
459 ///
460 /// Draws a pattern
461 ///
__draw_pattern(QPainter & painter)462 void DrumPatternEditor::__draw_pattern(QPainter& painter)
463 {
464 const UIStyle *pStyle = Preferences::get_instance()->getDefaultUIStyle();
465 const QColor selectedRowColor( pStyle->m_patternEditor_selectedRowColor.getRed(), pStyle->m_patternEditor_selectedRowColor.getGreen(), pStyle->m_patternEditor_selectedRowColor.getBlue() );
466
467 __create_background( painter );
468
469 if (m_pPattern == nullptr) {
470 return;
471 }
472
473 int nNotes = m_pPattern->get_length();
474 int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
475 Song *pSong = Hydrogen::get_instance()->getSong();
476
477 InstrumentList * pInstrList = pSong->get_instrument_list();
478
479
480 if ( m_nEditorHeight != (int)( m_nGridHeight * pInstrList->size() ) ) {
481 // the number of instruments is changed...recreate all
482 m_nEditorHeight = m_nGridHeight * pInstrList->size();
483 resize( width(), m_nEditorHeight );
484 }
485
486 for ( uint nInstr = 0; nInstr < pInstrList->size(); ++nInstr ) {
487 uint y = m_nGridHeight * nInstr;
488 if ( nInstr == (uint)nSelectedInstrument ) { // selected instrument
489 painter.fillRect( 0, y + 1, ( 20 + nNotes * m_nGridWidth ), m_nGridHeight - 1, selectedRowColor );
490 }
491 }
492
493
494 // draw the grid
495 __draw_grid( painter );
496
497
498 /*
499 BUGFIX
500
501 if m_pPattern is not renewed every time we draw a note,
502 hydrogen will crash after you save a song and create a new one.
503 -smoors
504 */
505 Hydrogen *pEngine = Hydrogen::get_instance();
506 PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
507 int nSelectedPatternNumber = pEngine->getSelectedPatternNumber();
508 if ( (nSelectedPatternNumber != -1) && ( (uint)nSelectedPatternNumber < pPatternList->size() ) ) {
509 m_pPattern = pPatternList->get( nSelectedPatternNumber );
510 }
511 else {
512 m_pPattern = nullptr;
513 }
514 // ~ FIX
515
516
517
518 if( m_pPattern->get_notes()->size() == 0) return;
519
520 const Pattern::notes_t* notes = m_pPattern->get_notes();
521 FOREACH_NOTE_CST_IT_BEGIN_END(notes,it) {
522 Note *note = it->second;
523 assert( note );
524 __draw_note( note, painter );
525 }
526 }
527
528
529
computeNoteColor(float velocity)530 QColor DrumPatternEditor::computeNoteColor( float velocity ){
531 int red;
532 int green;
533 int blue;
534
535
536 /*
537 The note gets painted black if it has the default velocity (0.8).
538 The color changes if you alter the velocity..
539 */
540
541 //qDebug() << "x: " << x;
542 //qDebug() << "x2: " << x*x;
543
544
545 if( velocity < 0.8){
546 red = fabs(-( velocity - 0.8))*255;
547 green = fabs(-( velocity - 0.8))*255;
548 blue = green * 1.25;
549 } else {
550 green = blue = 0;
551 red = (velocity-0.8)*5*255;
552 }
553
554 //qDebug() << "R " << red << "G " << green << "blue " << blue;
555 return QColor( red, green, blue );
556 }
557
558
559
560 ///
561 /// Draws a note
562 ///
__draw_note(Note * note,QPainter & p)563 void DrumPatternEditor::__draw_note( Note *note, QPainter& p )
564 {
565 static const UIStyle *pStyle = Preferences::get_instance()->getDefaultUIStyle();
566 static const QColor noteColor( pStyle->m_patternEditor_noteColor.getRed(), pStyle->m_patternEditor_noteColor.getGreen(), pStyle->m_patternEditor_noteColor.getBlue() );
567 static const QColor noteoffColor( pStyle->m_patternEditor_noteoffColor.getRed(), pStyle->m_patternEditor_noteoffColor.getGreen(), pStyle->m_patternEditor_noteoffColor.getBlue() );
568
569 p.setRenderHint( QPainter::Antialiasing );
570
571 int nInstrument = -1;
572 InstrumentList * pInstrList = Hydrogen::get_instance()->getSong()->get_instrument_list();
573 for ( uint nInstr = 0; nInstr < pInstrList->size(); ++nInstr ) {
574 Instrument *pInstr = pInstrList->get( nInstr );
575 if ( pInstr == note->get_instrument() ) {
576 nInstrument = nInstr;
577 break;
578 }
579 }
580 if ( nInstrument == -1 ) {
581 ERRORLOG( "Instrument not found..skipping note" );
582 return;
583 }
584
585 uint pos = note->get_position();
586
587 p.setPen( noteColor );
588
589
590 QColor color = computeNoteColor( note->get_velocity() );
591
592 uint w = 8;
593 uint h = m_nGridHeight / 3;
594
595 if ( note->get_length() == -1 && note->get_note_off() == false ) { // trigger note
596 uint x_pos = 20 + (pos * m_nGridWidth);// - m_nGridWidth / 2.0;
597 uint y_pos = ( nInstrument * m_nGridHeight) + (m_nGridHeight / 2) - 3;
598 p.setBrush( color );
599 p.drawEllipse( x_pos -4 , y_pos, w, h );
600
601
602 }
603 else if ( note->get_length() == 1 && note->get_note_off() == true ){
604 p.setPen( noteoffColor );
605 uint x_pos = 20 + ( pos * m_nGridWidth );// - m_nGridWidth / 2.0;
606
607 uint y_pos = ( nInstrument * m_nGridHeight ) + (m_nGridHeight / 2) - 3;
608 p.setBrush(QColor( noteoffColor));
609 p.drawEllipse( x_pos -4 , y_pos, w, h );
610
611
612
613 }
614 else {
615 float fNotePitch = note->get_octave() * 12 + note->get_key();
616 float fStep = pow( 1.0594630943593, ( double )fNotePitch );
617
618 uint x = 20 + (pos * m_nGridWidth);
619 int w = m_nGridWidth * note->get_length() / fStep;
620 w = w - 1; // lascio un piccolo spazio tra una nota ed un altra
621
622 int y = (int) ( ( nInstrument ) * m_nGridHeight + (m_nGridHeight / 100.0 * 30.0) );
623 int h = (int) (m_nGridHeight - ((m_nGridHeight / 100.0 * 30.0) * 2.0) );
624 p.setBrush( color );
625 p.fillRect( x, y + 1, w, h + 1, color ); /// \todo: definire questo colore nelle preferenze
626 p.drawRect( x, y + 1, w, h + 1 );
627 }
628 }
629
630
631
632
__draw_grid(QPainter & p)633 void DrumPatternEditor::__draw_grid( QPainter& p )
634 {
635 static const UIStyle *pStyle = Preferences::get_instance()->getDefaultUIStyle();
636 static const QColor res_1( pStyle->m_patternEditor_line1Color.getRed(), pStyle->m_patternEditor_line1Color.getGreen(), pStyle->m_patternEditor_line1Color.getBlue() );
637 static const QColor res_2( pStyle->m_patternEditor_line2Color.getRed(), pStyle->m_patternEditor_line2Color.getGreen(), pStyle->m_patternEditor_line2Color.getBlue() );
638 static const QColor res_3( pStyle->m_patternEditor_line3Color.getRed(), pStyle->m_patternEditor_line3Color.getGreen(), pStyle->m_patternEditor_line3Color.getBlue() );
639 static const QColor res_4( pStyle->m_patternEditor_line4Color.getRed(), pStyle->m_patternEditor_line4Color.getGreen(), pStyle->m_patternEditor_line4Color.getBlue() );
640 static const QColor res_5( pStyle->m_patternEditor_line5Color.getRed(), pStyle->m_patternEditor_line5Color.getGreen(), pStyle->m_patternEditor_line5Color.getBlue() );
641
642 // vertical lines
643 p.setPen( QPen( res_1, 0, Qt::DotLine ) );
644
645 int nBase;
646 if (m_bUseTriplets) {
647 nBase = 3;
648 }
649 else {
650 nBase = 4;
651 }
652
653 int n4th = 4 * MAX_NOTES / (nBase * 4);
654 int n8th = 4 * MAX_NOTES / (nBase * 8);
655 int n16th = 4 * MAX_NOTES / (nBase * 16);
656 int n32th = 4 * MAX_NOTES / (nBase * 32);
657 int n64th = 4 * MAX_NOTES / (nBase * 64);
658
659 int nNotes = MAX_NOTES;
660 if ( m_pPattern ) {
661 nNotes = m_pPattern->get_length();
662 }
663 if (!m_bUseTriplets) {
664 for ( int i = 0; i < nNotes + 1; i++ ) {
665 uint x = 20 + i * m_nGridWidth;
666
667 if ( (i % n4th) == 0 ) {
668 if (m_nResolution >= 4) {
669 p.setPen( QPen( res_1, 0 ) );
670 p.drawLine(x, 1, x, m_nEditorHeight - 1);
671 }
672 }
673 else if ( (i % n8th) == 0 ) {
674 if (m_nResolution >= 8) {
675 p.setPen( QPen( res_2, 0 ) );
676 p.drawLine(x, 1, x, m_nEditorHeight - 1);
677 }
678 }
679 else if ( (i % n16th) == 0 ) {
680 if (m_nResolution >= 16) {
681 p.setPen( QPen( res_3, 0 ) );
682 p.drawLine(x, 1, x, m_nEditorHeight - 1);
683 }
684 }
685 else if ( (i % n32th) == 0 ) {
686 if (m_nResolution >= 32) {
687 p.setPen( QPen( res_4, 0 ) );
688 p.drawLine(x, 1, x, m_nEditorHeight - 1);
689 }
690 }
691 else if ( (i % n64th) == 0 ) {
692 if (m_nResolution >= 64) {
693 p.setPen( QPen( res_5, 0 ) );
694 p.drawLine(x, 1, x, m_nEditorHeight - 1);
695 }
696 }
697 }
698 }
699 else { // Triplets
700 uint nCounter = 0;
701 int nSize = 4 * MAX_NOTES / (nBase * m_nResolution);
702
703 for ( int i = 0; i < nNotes + 1; i++ ) {
704 uint x = 20 + i * m_nGridWidth;
705
706 if ( (i % nSize) == 0) {
707 if ((nCounter % 3) == 0) {
708 p.setPen( QPen( res_1, 0 ) );
709 }
710 else {
711 p.setPen( QPen( res_3, 0 ) );
712 }
713 p.drawLine(x, 1, x, m_nEditorHeight - 1);
714 nCounter++;
715 }
716 }
717 }
718
719
720 // fill the first half of the rect with a solid color
721 static const QColor backgroundColor( pStyle->m_patternEditor_backgroundColor.getRed(), pStyle->m_patternEditor_backgroundColor.getGreen(), pStyle->m_patternEditor_backgroundColor.getBlue() );
722 static const QColor selectedRowColor( pStyle->m_patternEditor_selectedRowColor.getRed(), pStyle->m_patternEditor_selectedRowColor.getGreen(), pStyle->m_patternEditor_selectedRowColor.getBlue() );
723 int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
724 Song *pSong = Hydrogen::get_instance()->getSong();
725 int nInstruments = pSong->get_instrument_list()->size();
726 for ( uint i = 0; i < (uint)nInstruments; i++ ) {
727 uint y = m_nGridHeight * i + 1;
728 if ( i == (uint)nSelectedInstrument ) {
729 p.fillRect( 0, y, (20 + nNotes * m_nGridWidth), (int)( m_nGridHeight * 0.7 ), selectedRowColor );
730 }
731 else {
732 p.fillRect( 0, y, (20 + nNotes * m_nGridWidth), (int)( m_nGridHeight * 0.7 ), backgroundColor );
733 }
734 }
735
736 }
737
738
__create_background(QPainter & p)739 void DrumPatternEditor::__create_background( QPainter& p)
740 {
741 static const UIStyle *pStyle = Preferences::get_instance()->getDefaultUIStyle();
742 static const QColor backgroundColor( pStyle->m_patternEditor_backgroundColor.getRed(), pStyle->m_patternEditor_backgroundColor.getGreen(), pStyle->m_patternEditor_backgroundColor.getBlue() );
743 static const QColor alternateRowColor( pStyle->m_patternEditor_alternateRowColor.getRed(), pStyle->m_patternEditor_alternateRowColor.getGreen(), pStyle->m_patternEditor_alternateRowColor.getBlue() );
744 static const QColor lineColor( pStyle->m_patternEditor_lineColor.getRed(), pStyle->m_patternEditor_lineColor.getGreen(), pStyle->m_patternEditor_lineColor.getBlue() );
745
746 int nNotes = MAX_NOTES;
747 if ( m_pPattern ) {
748 nNotes = m_pPattern->get_length();
749 }
750
751 Song *pSong = Hydrogen::get_instance()->getSong();
752 int nInstruments = pSong->get_instrument_list()->size();
753
754 if ( m_nEditorHeight != (int)( m_nGridHeight * nInstruments ) ) {
755 // the number of instruments is changed...recreate all
756 m_nEditorHeight = m_nGridHeight * nInstruments;
757 resize( width(), m_nEditorHeight );
758 }
759
760 p.fillRect(0, 0, 20 + nNotes * m_nGridWidth, height(), backgroundColor);
761 for ( uint i = 0; i < (uint)nInstruments; i++ ) {
762 uint y = m_nGridHeight * i;
763 if ( ( i % 2) != 0) {
764 p.fillRect( 0, y, (20 + nNotes * m_nGridWidth), m_nGridHeight, alternateRowColor );
765 }
766 }
767
768 // horizontal lines
769 p.setPen( lineColor );
770 for ( uint i = 0; i < (uint)nInstruments; i++ ) {
771 uint y = m_nGridHeight * i + m_nGridHeight;
772 p.drawLine( 0, y, (20 + nNotes * m_nGridWidth), y);
773 }
774
775 p.drawLine( 0, m_nEditorHeight, (20 + nNotes * m_nGridWidth), m_nEditorHeight );
776 }
777
778
779
paintEvent(QPaintEvent *)780 void DrumPatternEditor::paintEvent( QPaintEvent* /*ev*/ )
781 {
782 //INFOLOG( "paint" );
783 //QWidget::paintEvent(ev);
784
785 QPainter painter( this );
786 __draw_pattern( painter );
787 }
788
789
790
791
792
793
showEvent(QShowEvent * ev)794 void DrumPatternEditor::showEvent ( QShowEvent *ev )
795 {
796 UNUSED( ev );
797 updateEditor();
798 }
799
800
801
hideEvent(QHideEvent * ev)802 void DrumPatternEditor::hideEvent ( QHideEvent *ev )
803 {
804 UNUSED( ev );
805 }
806
807
808
setResolution(uint res,bool bUseTriplets)809 void DrumPatternEditor::setResolution(uint res, bool bUseTriplets)
810 {
811 this->m_nResolution = res;
812 this->m_bUseTriplets = bUseTriplets;
813
814 // redraw all
815 update( 0, 0, width(), height() );
816 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
817 m_pPatternEditorPanel->getPanEditor()->updateEditor();
818 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
819 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
820 }
821
822
823
zoom_in()824 void DrumPatternEditor::zoom_in()
825 {
826 if (m_nGridWidth >= 3){
827 m_nGridWidth *= 2;
828 }else
829 {
830 m_nGridWidth *= 1.5;
831 }
832 updateEditor();
833 }
834
835
836
zoom_out()837 void DrumPatternEditor::zoom_out()
838 {
839 if ( m_nGridWidth > 1.5 ) {
840 if (m_nGridWidth > 3){
841 m_nGridWidth /= 2;
842 }else
843 {
844 m_nGridWidth /= 1.5;
845 }
846 updateEditor();
847 }
848 }
849
selectedInstrumentChangedEvent()850 void DrumPatternEditor::selectedInstrumentChangedEvent()
851 {
852 update( 0, 0, width(), height() );
853 }
854
855
856 /// This method is called from another thread (audio engine)
patternModifiedEvent()857 void DrumPatternEditor::patternModifiedEvent()
858 {
859 update( 0, 0, width(), height() );
860 }
861
862
patternChangedEvent()863 void DrumPatternEditor::patternChangedEvent()
864 {
865 updateEditor();
866 }
867
868
selectedPatternChangedEvent()869 void DrumPatternEditor::selectedPatternChangedEvent()
870 {
871 updateEditor();
872 }
873
874
875 ///NotePropertiesRuler undo redo action
undoRedoAction(int column,QString mode,int nSelectedPatternNumber,int nSelectedInstrument,float velocity,float pan_L,float pan_R,float leadLag,float probability,int noteKeyVal,int octaveKeyVal)876 void DrumPatternEditor::undoRedoAction( int column,
877 QString mode,
878 int nSelectedPatternNumber,
879 int nSelectedInstrument,
880 float velocity,
881 float pan_L,
882 float pan_R,
883 float leadLag,
884 float probability,
885 int noteKeyVal,
886 int octaveKeyVal)
887 {
888 Hydrogen *pEngine = Hydrogen::get_instance();
889 Song *pSong = pEngine->getSong();
890 Pattern *pPattern;
891 PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
892 if ( (nSelectedPatternNumber != -1) && ( (uint)nSelectedPatternNumber < pPatternList->size() ) ) {
893 pPattern = pPatternList->get( nSelectedPatternNumber );
894 }
895 else {
896 pPattern = nullptr;
897 }
898
899 const Pattern::notes_t* notes = pPattern->get_notes();
900 FOREACH_NOTE_CST_IT_BOUND(notes,it,column) {
901 Note *pNote = it->second;
902 assert( pNote );
903 assert( (int)pNote->get_position() == column );
904 if ( pNote->get_instrument() != pSong->get_instrument_list()->get( nSelectedInstrument ) ) {
905 continue;
906 }
907
908 if ( mode == "VELOCITY" && !pNote->get_note_off() ) {
909 pNote->set_velocity( velocity );
910 }
911 else if ( mode == "PAN" ){
912
913 pNote->set_pan_l( pan_L );
914 pNote->set_pan_r( pan_R );
915 }
916 else if ( mode == "LEADLAG" ){
917 pNote->set_lead_lag( leadLag );
918 }
919 else if ( mode == "NOTEKEY" ){
920 pNote->set_key_octave( (Note::Key)noteKeyVal, (Note::Octave)octaveKeyVal );
921 }
922 else if ( mode == "PROBABILITY" ){
923 pNote->set_probability( probability );
924 }
925
926 pSong->set_is_modified( true );
927 break;
928 }
929 updateEditor();
930 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
931 m_pPatternEditorPanel->getPanEditor()->updateEditor();
932 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
933 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
934 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
935 m_pPatternEditorPanel->getProbabilityEditor()->updateEditor();
936
937 }
938
939
940 ///==========================================================
941 ///undo / redo actions from pattern editor instrument list
942
functionClearNotesRedoAction(int nSelectedInstrument,int patternNumber)943 void DrumPatternEditor::functionClearNotesRedoAction( int nSelectedInstrument, int patternNumber )
944 {
945 Hydrogen * H = Hydrogen::get_instance();
946 PatternList *pPatternList = Hydrogen::get_instance()->getSong()->get_pattern_list();
947 Pattern *pPattern = pPatternList->get( patternNumber );
948
949 Instrument *pSelectedInstrument = H->getSong()->get_instrument_list()->get( nSelectedInstrument );
950
951 pPattern->purge_instrument( pSelectedInstrument );
952 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
953 }
954
955
956
functionClearNotesUndoAction(std::list<H2Core::Note * > noteList,int nSelectedInstrument,int patternNumber)957 void DrumPatternEditor::functionClearNotesUndoAction( std::list< H2Core::Note* > noteList, int nSelectedInstrument, int patternNumber )
958 {
959 Hydrogen * H = Hydrogen::get_instance();
960 PatternList *pPatternList = H->getSong()->get_pattern_list();
961 Pattern *pPattern = pPatternList->get( patternNumber );
962
963 std::list < H2Core::Note *>::const_iterator pos;
964 for ( pos = noteList.begin(); pos != noteList.end(); ++pos){
965 Note *pNote;
966 pNote = new Note(*pos);
967 assert( pNote );
968 pPattern->insert_note( pNote );
969 }
970 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
971 updateEditor();
972 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
973 m_pPatternEditorPanel->getPanEditor()->updateEditor();
974 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
975 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
976 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
977
978 }
979
functionPasteNotesUndoAction(std::list<H2Core::Pattern * > & appliedList)980 void DrumPatternEditor::functionPasteNotesUndoAction(std::list<H2Core::Pattern*> & appliedList)
981 {
982 // Get song's pattern list
983 Hydrogen * H = Hydrogen::get_instance();
984 PatternList *patternList = H->getSong()->get_pattern_list();
985
986 AudioEngine::get_instance()->lock( RIGHT_HERE ); // lock the audio engine
987
988 while (appliedList.size() > 0)
989 {
990 // Get next applied pattern
991 Pattern *pApplied = appliedList.front();
992 assert(pApplied);
993
994 // Find destination pattern to perform undo
995 Pattern *pat = patternList->find(pApplied->get_name());
996
997 if (pat != NULL)
998 {
999 // Remove all notes of applied pattern from destination pattern
1000 const Pattern::notes_t* notes = pApplied->get_notes();
1001 FOREACH_NOTE_CST_IT_BEGIN_END(notes, it)
1002 {
1003 // Get note to remove
1004 Note *pNote = it->second;
1005 assert(pNote);
1006
1007 // Check if note is not present
1008 Pattern::notes_t* notes = (Pattern::notes_t *)pat->get_notes();
1009 FOREACH_NOTE_IT_BOUND(notes, it, pNote->get_position())
1010 {
1011 Note *pFoundNote = it->second;
1012 if (pFoundNote->get_instrument() == pNote->get_instrument())
1013 {
1014 notes->erase(it);
1015 delete pFoundNote;
1016 break;
1017 }
1018 }
1019 }
1020 }
1021
1022
1023 // Remove applied pattern;
1024 delete pApplied;
1025 appliedList.pop_front();
1026 }
1027
1028 AudioEngine::get_instance()->unlock(); // unlock the audio engine
1029
1030 // Update editors
1031 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
1032 updateEditor();
1033 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
1034 m_pPatternEditorPanel->getPanEditor()->updateEditor();
1035 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
1036 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
1037 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
1038 }
1039
functionPasteNotesRedoAction(std::list<H2Core::Pattern * > & changeList,std::list<H2Core::Pattern * > & appliedList)1040 void DrumPatternEditor::functionPasteNotesRedoAction(std::list<H2Core::Pattern*> & changeList, std::list<H2Core::Pattern*> & appliedList)
1041 {
1042 Hydrogen * H = Hydrogen::get_instance();
1043 PatternList *patternList = H->getSong()->get_pattern_list();
1044
1045 AudioEngine::get_instance()->lock( RIGHT_HERE ); // lock the audio engine
1046
1047 // Add notes to pattern
1048 std::list < H2Core::Pattern *>::iterator pos;
1049 for ( pos = changeList.begin(); pos != changeList.end(); ++pos)
1050 {
1051 Pattern *pPattern = *pos;
1052 assert(pPattern);
1053
1054 Pattern *pat = patternList->find(pPattern->get_name()); // Destination pattern
1055
1056 if (pat != NULL)
1057 {
1058 // Create applied pattern
1059 Pattern *pApplied = new Pattern(
1060 pat->get_name(),
1061 pat->get_info(),
1062 pat->get_category(),
1063 pat->get_length());
1064
1065 // Add all notes of source pattern to destination pattern
1066 // and store all applied notes in applied pattern
1067 const Pattern::notes_t* notes = pPattern->get_notes();
1068 FOREACH_NOTE_CST_IT_BEGIN_END(notes, it)
1069 {
1070 Note *pNote = it->second;
1071 assert(pNote);
1072
1073 // Check if note is not present
1074 bool noteExists = false;
1075 const Pattern::notes_t* notes = pat->get_notes();
1076 FOREACH_NOTE_CST_IT_BOUND(notes, it, pNote->get_position())
1077 {
1078 Note *pFoundNote = it->second;
1079 if (pFoundNote->get_instrument() == pNote->get_instrument())
1080 {
1081 // note already exists
1082 noteExists = true;
1083 break;
1084 }
1085 }
1086
1087 // Apply note and store it as applied
1088 if (!noteExists)
1089 {
1090 pat->insert_note(new Note(pNote));
1091 pApplied->insert_note(new Note(pNote));
1092 }
1093 }
1094
1095 // Add applied pattern to applied list
1096 appliedList.push_back(pApplied);
1097 }
1098 }
1099 AudioEngine::get_instance()->unlock(); // unlock the audio engine
1100
1101 // Update editors
1102 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
1103 updateEditor();
1104 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
1105 m_pPatternEditorPanel->getPanEditor()->updateEditor();
1106 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
1107 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
1108 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
1109 }
1110
1111
1112
functionFillNotesUndoAction(QStringList noteList,int nSelectedInstrument,int patternNumber)1113 void DrumPatternEditor::functionFillNotesUndoAction( QStringList noteList, int nSelectedInstrument, int patternNumber )
1114 {
1115 Hydrogen * H = Hydrogen::get_instance();
1116 PatternList *pPatternList = Hydrogen::get_instance()->getSong()->get_pattern_list();
1117 Pattern *pPattern = pPatternList->get( patternNumber );
1118 Instrument *pSelectedInstrument = H->getSong()->get_instrument_list()->get( nSelectedInstrument );
1119
1120 AudioEngine::get_instance()->lock( RIGHT_HERE ); // lock the audio engine
1121
1122 for (int i = 0; i < noteList.size(); i++ ) {
1123 int nColumn = noteList.value(i).toInt();
1124 Pattern::notes_t* notes = (Pattern::notes_t*)pPattern->get_notes();
1125 FOREACH_NOTE_IT_BOUND(notes,it,nColumn) {
1126 Note *pNote = it->second;
1127 assert( pNote );
1128 if ( pNote->get_instrument() == pSelectedInstrument ) {
1129 // the note exists...remove it!
1130 notes->erase( it );
1131 delete pNote;
1132 break;
1133 }
1134 }
1135 }
1136 AudioEngine::get_instance()->unlock(); // unlock the audio engine
1137
1138 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
1139 updateEditor();
1140 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
1141 m_pPatternEditorPanel->getPanEditor()->updateEditor();
1142 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
1143 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
1144 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
1145 }
1146
1147
functionFillNotesRedoAction(QStringList noteList,int nSelectedInstrument,int patternNumber)1148 void DrumPatternEditor::functionFillNotesRedoAction( QStringList noteList, int nSelectedInstrument, int patternNumber )
1149 {
1150 Hydrogen * H = Hydrogen::get_instance();
1151 PatternList *pPatternList = Hydrogen::get_instance()->getSong()->get_pattern_list();
1152 Pattern *pPattern = pPatternList->get( patternNumber );
1153 Instrument *pSelectedInstrument = H->getSong()->get_instrument_list()->get( nSelectedInstrument );
1154
1155 const float velocity = 0.8f;
1156 const float pan_L = 0.5f;
1157 const float pan_R = 0.5f;
1158 const float fPitch = 0.0f;
1159 const int nLength = -1;
1160
1161 AudioEngine::get_instance()->lock( RIGHT_HERE ); // lock the audio engine
1162 for (int i = 0; i < noteList.size(); i++ ) {
1163
1164 // create the new note
1165 int position = noteList.value(i).toInt();
1166 Note *pNote = new Note( pSelectedInstrument, position, velocity, pan_L, pan_R, nLength, fPitch );
1167 pPattern->insert_note( pNote );
1168 }
1169 AudioEngine::get_instance()->unlock(); // unlock the audio engine
1170
1171 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
1172 updateEditor();
1173 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
1174 m_pPatternEditorPanel->getPanEditor()->updateEditor();
1175 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
1176 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
1177 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
1178 }
1179
1180
functionRandomVelocityAction(QStringList noteVeloValue,int nSelectedInstrument,int selectedPatternNumber)1181 void DrumPatternEditor::functionRandomVelocityAction( QStringList noteVeloValue, int nSelectedInstrument, int selectedPatternNumber )
1182 {
1183 Hydrogen * H = Hydrogen::get_instance();
1184 PatternList *pPatternList = Hydrogen::get_instance()->getSong()->get_pattern_list();
1185 Pattern *pPattern = pPatternList->get( selectedPatternNumber );
1186 Instrument *pSelectedInstrument = H->getSong()->get_instrument_list()->get( nSelectedInstrument );
1187
1188
1189 AudioEngine::get_instance()->lock( RIGHT_HERE ); // lock the audio engine
1190
1191 int nBase;
1192 if ( isUsingTriplets() ) {
1193 nBase = 3;
1194 }
1195 else {
1196 nBase = 4;
1197 }
1198
1199 int nResolution = 4 * MAX_NOTES / ( nBase * getResolution() );
1200 int positionCount = 0;
1201 for (int i = 0; i < pPattern->get_length(); i += nResolution) {
1202 const Pattern::notes_t* notes = pPattern->get_notes();
1203 FOREACH_NOTE_CST_IT_BOUND(notes,it,i) {
1204 Note *pNote = it->second;
1205 if ( pNote->get_instrument() == pSelectedInstrument) {
1206 float velocity = noteVeloValue.value( positionCount ).toFloat();
1207 pNote->set_velocity(velocity);
1208 positionCount++;
1209 }
1210 }
1211 }
1212 AudioEngine::get_instance()->unlock(); // unlock the audio engine
1213
1214 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
1215 updateEditor();
1216 m_pPatternEditorPanel->getVelocityEditor()->updateEditor();
1217 m_pPatternEditorPanel->getPanEditor()->updateEditor();
1218 m_pPatternEditorPanel->getLeadLagEditor()->updateEditor();
1219 m_pPatternEditorPanel->getNoteKeyEditor()->updateEditor();
1220 m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
1221 }
1222
1223
functionMoveInstrumentAction(int nSourceInstrument,int nTargetInstrument)1224 void DrumPatternEditor::functionMoveInstrumentAction( int nSourceInstrument, int nTargetInstrument )
1225 {
1226 Hydrogen *engine = Hydrogen::get_instance();
1227 AudioEngine::get_instance()->lock( RIGHT_HERE );
1228
1229 Song *pSong = engine->getSong();
1230 InstrumentList *pInstrumentList = pSong->get_instrument_list();
1231
1232 if ( ( nTargetInstrument > (int)pInstrumentList->size() ) || ( nTargetInstrument < 0) ) {
1233 AudioEngine::get_instance()->unlock();
1234 return;
1235 }
1236
1237 pInstrumentList->move( nSourceInstrument, nTargetInstrument );
1238
1239 #ifdef H2CORE_HAVE_JACK
1240 engine->renameJackPorts( pSong );
1241 #endif
1242
1243 AudioEngine::get_instance()->unlock();
1244 engine->setSelectedInstrumentNumber( nTargetInstrument );
1245
1246 pSong->set_is_modified( true );
1247 }
1248
1249
functionDropInstrumentUndoAction(int nTargetInstrument,std::vector<int> * AddedComponents)1250 void DrumPatternEditor::functionDropInstrumentUndoAction( int nTargetInstrument, std::vector<int>* AddedComponents )
1251 {
1252 Hydrogen *pEngine = Hydrogen::get_instance();
1253 pEngine->removeInstrument( nTargetInstrument, false );
1254
1255 std::vector<DrumkitComponent*>* pDrumkitComponents = pEngine->getSong()->get_components();
1256
1257 for (std::vector<int>::iterator it = AddedComponents->begin() ; it != AddedComponents->end(); ++it) {
1258 int p_compoID = *it;
1259
1260 for ( int n = 0 ; n < pDrumkitComponents->size() ; n++ ) {
1261 DrumkitComponent* pTmpDrumkitComponent = pDrumkitComponents->at( n );
1262 if( pTmpDrumkitComponent->get_id() == p_compoID ) {
1263 pDrumkitComponents->erase( pDrumkitComponents->begin() + n );
1264 break;
1265 }
1266 }
1267 }
1268
1269 AudioEngine::get_instance()->lock( RIGHT_HERE );
1270 #ifdef H2CORE_HAVE_JACK
1271 Song *pSong = pEngine->getSong();
1272 pEngine->renameJackPorts(pSong);
1273 #endif
1274 AudioEngine::get_instance()->unlock();
1275 updateEditor();
1276 }
1277
1278
functionDropInstrumentRedoAction(QString sDrumkitName,QString sInstrumentName,int nTargetInstrument,std::vector<int> * AddedComponents)1279 void DrumPatternEditor::functionDropInstrumentRedoAction( QString sDrumkitName, QString sInstrumentName, int nTargetInstrument, std::vector<int>* AddedComponents)
1280 {
1281 Instrument *pNewInstrument = Instrument::load_instrument( sDrumkitName, sInstrumentName );
1282 if( pNewInstrument == nullptr ){
1283 return;
1284 }
1285
1286 Drumkit *pNewDrumkit = Drumkit::load_by_name( sDrumkitName, false );
1287 if( pNewDrumkit == nullptr ){
1288 return;
1289 }
1290
1291 Hydrogen *pEngine = Hydrogen::get_instance();
1292
1293 AudioEngine::get_instance()->lock( RIGHT_HERE );
1294
1295 std::vector<InstrumentComponent*>* pOldInstrumentComponents = new std::vector<InstrumentComponent*> ( pNewInstrument->get_components()->begin(), pNewInstrument->get_components()->end() );
1296 pNewInstrument->get_components()->clear();
1297
1298 for (std::vector<DrumkitComponent*>::iterator it = pNewDrumkit->get_components()->begin() ; it != pNewDrumkit->get_components()->end(); ++it) {
1299 DrumkitComponent* pComponent = *it;
1300 int OldID = pComponent->get_id();
1301 int NewID = -1;
1302
1303 NewID = findExistingCompo( pComponent->get_name() );
1304
1305 if ( NewID == -1 ) {
1306 NewID = findFreeCompoID();
1307
1308 AddedComponents->push_back( NewID );
1309
1310 pComponent->set_id( NewID );
1311 pComponent->set_name( renameCompo( pComponent->get_name() ) );
1312 Hydrogen::get_instance()->getSong()->get_components()->push_back( pComponent );
1313 }
1314
1315 for ( std::vector<InstrumentComponent*>::iterator it2 = pOldInstrumentComponents->begin() ; it2 != pOldInstrumentComponents->end(); ++it2 ) {
1316 InstrumentComponent* pOldInstrCompo = *it2;
1317 if( pOldInstrCompo->get_drumkit_componentID() == OldID ) {
1318 InstrumentComponent* pNewInstrCompo = new InstrumentComponent( pOldInstrCompo );
1319 pNewInstrCompo->set_drumkit_componentID( NewID );
1320
1321 pNewInstrument->get_components()->push_back( pNewInstrCompo );
1322 }
1323 }
1324 }
1325
1326 // create a new valid ID for this instrument
1327 int nID = -1;
1328 for ( uint i = 0; i < pEngine->getSong()->get_instrument_list()->size(); ++i ) {
1329 Instrument* pInstr = pEngine->getSong()->get_instrument_list()->get( i );
1330 if ( pInstr->get_id() > nID ) {
1331 nID = pInstr->get_id();
1332 }
1333 }
1334 ++nID;
1335
1336 pNewInstrument->set_id( nID );
1337
1338 pEngine->getSong()->get_instrument_list()->add( pNewInstrument );
1339
1340 #ifdef H2CORE_HAVE_JACK
1341 pEngine->renameJackPorts( pEngine->getSong() );
1342 #endif
1343
1344 AudioEngine::get_instance()->unlock();
1345 //move instrument to the position where it was dropped
1346 functionMoveInstrumentAction(pEngine->getSong()->get_instrument_list()->size() - 1 , nTargetInstrument );
1347
1348 // select the new instrument
1349 pEngine->setSelectedInstrumentNumber(nTargetInstrument);
1350 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
1351 updateEditor();
1352 }
1353
renameCompo(QString OriginalName)1354 QString DrumPatternEditor::renameCompo( QString OriginalName )
1355 {
1356 std::vector<DrumkitComponent*>* pComponentList = Hydrogen::get_instance()->getSong()->get_components();
1357 for (std::vector<DrumkitComponent*>::iterator it = pComponentList->begin() ; it != pComponentList->end(); ++it) {
1358 DrumkitComponent* pComponent = *it;
1359 if( pComponent->get_name().compare( OriginalName ) == 0 ){
1360 return renameCompo( OriginalName + "_new" );
1361 }
1362 }
1363 return OriginalName;
1364 }
1365
findFreeCompoID(int startingPoint)1366 int DrumPatternEditor::findFreeCompoID( int startingPoint )
1367 {
1368 bool FoundFreeSlot = true;
1369 std::vector<DrumkitComponent*>* pComponentList = Hydrogen::get_instance()->getSong()->get_components();
1370 for (std::vector<DrumkitComponent*>::iterator it = pComponentList->begin() ; it != pComponentList->end(); ++it) {
1371 DrumkitComponent* pComponent = *it;
1372 if( pComponent->get_id() == startingPoint ) {
1373 FoundFreeSlot = false;
1374 break;
1375 }
1376 }
1377
1378 if(FoundFreeSlot){
1379 return startingPoint;
1380 } else {
1381 return findFreeCompoID( startingPoint + 1 );
1382 }
1383 }
1384
findExistingCompo(QString SourceName)1385 int DrumPatternEditor::findExistingCompo( QString SourceName )
1386 {
1387 std::vector<DrumkitComponent*>* pComponentList = Hydrogen::get_instance()->getSong()->get_components();
1388 for (std::vector<DrumkitComponent*>::iterator it = pComponentList->begin() ; it != pComponentList->end(); ++it) {
1389 DrumkitComponent* pComponent = *it;
1390 if ( pComponent->get_name().compare( SourceName ) == 0 ){
1391 return pComponent->get_id();
1392 }
1393 }
1394 return -1;
1395 }
1396
1397
1398
functionDeleteInstrumentUndoAction(std::list<H2Core::Note * > noteList,int nSelectedInstrument,QString instrumentName,QString drumkitName)1399 void DrumPatternEditor::functionDeleteInstrumentUndoAction( std::list< H2Core::Note* > noteList, int nSelectedInstrument, QString instrumentName, QString drumkitName )
1400 {
1401 Hydrogen *pEngine = Hydrogen::get_instance();
1402 Instrument *pNewInstrument;
1403 if( drumkitName == "" ){
1404 pNewInstrument = new Instrument( pEngine->getSong()->get_instrument_list()->size() -1, instrumentName );
1405 }else
1406 {
1407 pNewInstrument = Instrument::load_instrument( drumkitName, instrumentName );
1408 }
1409 if( pNewInstrument == nullptr ) return;
1410
1411 // create a new valid ID for this instrument
1412 int nID = -1;
1413 for ( uint i = 0; i < pEngine->getSong()->get_instrument_list()->size(); ++i ) {
1414 Instrument* pInstr = pEngine->getSong()->get_instrument_list()->get( i );
1415 if ( pInstr->get_id() > nID ) {
1416 nID = pInstr->get_id();
1417 }
1418 }
1419 ++nID;
1420
1421 pNewInstrument->set_id( nID );
1422 // pNewInstrument->set_adsr( new ADSR( 0, 0, 1.0, 1000 ) );
1423
1424 AudioEngine::get_instance()->lock( RIGHT_HERE );
1425 pEngine->getSong()->get_instrument_list()->add( pNewInstrument );
1426
1427 #ifdef H2CORE_HAVE_JACK
1428 pEngine->renameJackPorts( pEngine->getSong() );
1429 #endif
1430
1431 AudioEngine::get_instance()->unlock(); // unlock the audio engine
1432
1433 //move instrument to the position where it was dropped
1434 functionMoveInstrumentAction(pEngine->getSong()->get_instrument_list()->size() - 1 , nSelectedInstrument );
1435
1436 // select the new instrument
1437 pEngine->setSelectedInstrumentNumber( nSelectedInstrument );
1438
1439 H2Core::Pattern *pPattern;
1440 PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
1441
1442 updateEditor();
1443 EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
1444
1445 //restore all deleted instrument notes
1446 AudioEngine::get_instance()->lock( RIGHT_HERE );
1447 if(noteList.size() > 0 ){
1448 std::list < H2Core::Note *>::const_iterator pos;
1449 for ( pos = noteList.begin(); pos != noteList.end(); ++pos){
1450 Note *pNote = new Note( *pos, pNewInstrument );
1451 assert( pNote );
1452 pPattern = pPatternList->get( pNote->get_pattern_idx() );
1453 assert (pPattern);
1454 pPattern->insert_note( pNote );
1455 //delete pNote;
1456 }
1457 }
1458 AudioEngine::get_instance()->unlock(); // unlock the audio engine
1459 }
1460
functionAddEmptyInstrumentUndo()1461 void DrumPatternEditor::functionAddEmptyInstrumentUndo()
1462 {
1463
1464 Hydrogen *pEngine = Hydrogen::get_instance();
1465 pEngine->removeInstrument( pEngine->getSong()->get_instrument_list()->size() -1 , false );
1466
1467 AudioEngine::get_instance()->lock( RIGHT_HERE );
1468 #ifdef H2CORE_HAVE_JACK
1469 pEngine->renameJackPorts( pEngine->getSong() );
1470 #endif
1471 AudioEngine::get_instance()->unlock();
1472 updateEditor();
1473 }
1474
1475
functionAddEmptyInstrumentRedo()1476 void DrumPatternEditor::functionAddEmptyInstrumentRedo()
1477 {
1478 AudioEngine::get_instance()->lock( RIGHT_HERE );
1479 Song* pSong = Hydrogen::get_instance()->getSong();
1480 InstrumentList* pList = pSong->get_instrument_list();
1481
1482 // create a new valid ID for this instrument
1483 int nID = -1;
1484 for ( uint i = 0; i < pList->size(); ++i ) {
1485 Instrument* pInstr = pList->get( i );
1486 if ( pInstr->get_id() > nID ) {
1487 nID = pInstr->get_id();
1488 }
1489 }
1490 ++nID;
1491
1492 Instrument *pNewInstr = new Instrument( nID, "New instrument");
1493 pList->add( pNewInstr );
1494
1495 #ifdef H2CORE_HAVE_JACK
1496 Hydrogen::get_instance()->renameJackPorts( pSong );
1497 #endif
1498
1499 AudioEngine::get_instance()->unlock();
1500
1501 Hydrogen::get_instance()->setSelectedInstrumentNumber( pList->size() - 1 );
1502
1503 }
1504 ///~undo / redo actions from pattern editor instrument list
1505 ///==========================================================
1506