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