editing note volume and hearing clicked on notes is implemented more cleanly, editing note volume only affects selected notes (or all notes if none selected), when "scribbling" note volumes, if there is a chord, it will play all 3 notes, rather than just the first one
git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1909 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
14
ChangeLog
14
ChangeLog
@@ -1,3 +1,17 @@
|
||||
2008-12-12 Andrew Kelley <superjoe30/at/gmail/dot/com>
|
||||
|
||||
* include/note.h:
|
||||
* src/core/note.cpp:
|
||||
* include/piano_roll.h:
|
||||
* src/gui/piano_roll.cpp:
|
||||
* TODO:
|
||||
- editing note volume and hearing clicked on notes
|
||||
is implemented more cleanly
|
||||
- editing note volume only affects selected notes (or all notes if
|
||||
none selected)
|
||||
- when "scribbling" note volumes, if there is a chord, it will play all
|
||||
3 notes, rather than just the first one
|
||||
|
||||
2008-12-11 Tobias Doerffel <tobydox/at/users/dot/sourceforge/dot/net>
|
||||
|
||||
* include/automation_pattern.h:
|
||||
|
||||
22
TODO
22
TODO
@@ -52,21 +52,23 @@
|
||||
- add FLAC as export-format?
|
||||
|
||||
Andrew Kelley's todo:
|
||||
- recording automation
|
||||
- make knobs easier to tune (less sensitive)
|
||||
- make the menu for a channel happen when you right click, instead of renaming, and make the midi input a top-level menu item
|
||||
- add a "Set exact value" to a right clicked automation menu
|
||||
- enable "auto detect" by default when you bring up the "connect to controller" window
|
||||
|
||||
- piano roll: make the note volume section have adjustable height
|
||||
- add note panning ability to piano roll
|
||||
- paint piano roll notes with a darker color based on how high volume the note is
|
||||
- paint note volume blue if selected
|
||||
- multiview button - show notes from every instrument in the current beat+bassline with different colors
|
||||
- undo/redo for piano roll
|
||||
- add a tools menu to piano roll
|
||||
* put some of the tools on there that already have keyboard shortcuts (ctrl+up/down, shift+left/right)
|
||||
* humanizing tool
|
||||
* quick slice
|
||||
* look through FL Studio's tools and implement some of them
|
||||
- undo/redo for piano roll
|
||||
- make copy/paste work beyond inside piano roll - it didn't work when I copied notes from one pattern and then opened another pattern and pasted.
|
||||
- piano roll: make the note volume section have adjustable height
|
||||
- add note panning ability to piano roll
|
||||
|
||||
- recording automation
|
||||
- make knobs easier to tune (less sensitive)
|
||||
- make the menu for a channel happen when you right click, instead of renaming, and make the midi input a top-level menu item
|
||||
- add a "Set exact value" to a right clicked automation menu
|
||||
- enable "auto detect" by default when you bring up the "connect to controller" window
|
||||
|
||||
- "paintbrush" tool for the song editor, to easily "paint" beat+basslines
|
||||
- when you clone a track in the song editor, rename the track so that it doesn't have the same name (increment the number if necessary)
|
||||
|
||||
@@ -91,6 +91,7 @@ public:
|
||||
note( const note & _note );
|
||||
virtual ~note();
|
||||
|
||||
// used by GUI
|
||||
inline void setSelected( const bool _selected ){ m_selected = _selected; }
|
||||
inline void setOldKey( const int _oldKey ){ m_oldKey = _oldKey; }
|
||||
inline void setOldPos( const midiTime & _oldPos ){ m_oldPos = _oldPos; }
|
||||
@@ -98,6 +99,11 @@ public:
|
||||
{
|
||||
m_oldLength = _oldLength;
|
||||
}
|
||||
inline void setIsPlaying( const bool _isPlaying )
|
||||
{
|
||||
m_isPlaying = _isPlaying;
|
||||
}
|
||||
|
||||
|
||||
void setLength( const midiTime & _length );
|
||||
void setPos( const midiTime & _pos );
|
||||
@@ -133,6 +139,11 @@ public:
|
||||
{
|
||||
return m_oldLength;
|
||||
}
|
||||
|
||||
inline bool isPlaying( void ) const
|
||||
{
|
||||
return m_isPlaying;
|
||||
}
|
||||
|
||||
inline midiTime endPos( void ) const
|
||||
{
|
||||
@@ -212,11 +223,13 @@ private:
|
||||
ChangePosition
|
||||
} ;*/
|
||||
|
||||
|
||||
// for piano roll editing
|
||||
bool m_selected;
|
||||
int m_oldKey;
|
||||
midiTime m_oldPos;
|
||||
midiTime m_oldLength;
|
||||
bool m_isPlaying;
|
||||
|
||||
int m_key;
|
||||
volume m_volume;
|
||||
|
||||
@@ -169,6 +169,7 @@ private:
|
||||
void shiftPos(int amount);
|
||||
void shiftSemiTone(int amount);
|
||||
bool isSelection() const;
|
||||
void testPlayNote( note * n );
|
||||
|
||||
void dragNotes( int x, int y, bool alt );
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ note::note( const midiTime & _length, const midiTime & _pos,
|
||||
m_oldKey( tLimit( _key, 0, NumKeys ) ),
|
||||
m_oldPos( _pos ),
|
||||
m_oldLength( _length ),
|
||||
m_isPlaying( false ),
|
||||
m_key( tLimit( _key, 0, NumKeys ) ),
|
||||
m_volume( tLimit( _volume, MinVolume, MaxVolume ) ),
|
||||
m_panning( tLimit( _panning, PanningLeft, PanningRight ) ),
|
||||
@@ -72,6 +73,7 @@ note::note( const note & _note ) :
|
||||
m_oldKey( _note.m_oldKey ),
|
||||
m_oldPos( _note.m_oldPos ),
|
||||
m_oldLength( _note.m_oldLength ),
|
||||
m_isPlaying( _note.m_isPlaying ),
|
||||
m_key( _note.m_key),
|
||||
m_volume( _note.m_volume ),
|
||||
m_panning( _note.m_panning ),
|
||||
|
||||
@@ -1081,7 +1081,6 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
|
||||
if( _me->y() > PR_TOP_MARGIN )
|
||||
{
|
||||
bool play_note = ! engine::getSong()->isPlaying();
|
||||
volume vol = DefaultVolume;
|
||||
|
||||
bool edit_note = ( _me->y() > height() -
|
||||
@@ -1169,10 +1168,9 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
m_currentNote = *it;
|
||||
m_action = ActionChangeNoteVolume;
|
||||
key_num = ( *it )->key();
|
||||
}
|
||||
else
|
||||
{
|
||||
play_note = false;
|
||||
|
||||
testPlayNote( *it );
|
||||
|
||||
}
|
||||
}
|
||||
// left button??
|
||||
@@ -1280,17 +1278,12 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
// set resize-cursor
|
||||
QCursor c( Qt::SizeHorCursor );
|
||||
QApplication::setOverrideCursor( c );
|
||||
play_note = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise move it
|
||||
m_action = ActionMoveNote;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// set move-cursor
|
||||
QCursor c( Qt::SizeAllCursor );
|
||||
QApplication::setOverrideCursor( c );
|
||||
@@ -1328,6 +1321,9 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
engine::getSongEditor()->update();
|
||||
}
|
||||
}
|
||||
|
||||
// play the note
|
||||
testPlayNote( m_currentNote );
|
||||
}
|
||||
|
||||
|
||||
@@ -1346,7 +1342,6 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
{
|
||||
// erase single note
|
||||
m_mouseDownRight = true;
|
||||
play_note = false;
|
||||
if( it != notes.end() )
|
||||
{
|
||||
if( ( *it )->length() > 0 )
|
||||
@@ -1372,36 +1367,35 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
m_selectedKeys = 1;
|
||||
m_action = ActionSelectNotes;
|
||||
|
||||
play_note = false;
|
||||
|
||||
// call mousemove to fix glitch where selection
|
||||
// appears in wrong spot on mousedown
|
||||
mouseMoveEvent( _me );
|
||||
}
|
||||
else if( _me->button() == Qt::RightButton &&
|
||||
m_editMode == ModeMove )
|
||||
{
|
||||
// when clicking right in select-move, we
|
||||
// switch to draw-mode
|
||||
m_drawButton->setChecked( true );
|
||||
play_note = false;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
// was there an action where should be played the note?
|
||||
if( play_note == true && m_recording == false )
|
||||
{
|
||||
m_lastKey = key_num;
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOn, 0, key_num,
|
||||
vol * 127 / 100 ),
|
||||
midiTime() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pianoRoll::testPlayNote( note * n )
|
||||
{
|
||||
m_lastKey = n->key();
|
||||
|
||||
if(! n->isPlaying() && ! m_recording && ! engine::getSong()->isPlaying() )
|
||||
{
|
||||
n->setIsPlaying( true );
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOn, 0, n->key(),
|
||||
n->getVolume() * 127 / 100 ), midiTime() );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pianoRoll::computeSelectedNotes(bool shift)
|
||||
{
|
||||
if( m_selectStartTick == 0 &&
|
||||
@@ -1512,17 +1506,21 @@ void pianoRoll::mouseReleaseEvent( QMouseEvent * _me )
|
||||
|
||||
if( validPattern() == true )
|
||||
{
|
||||
if( m_action == ActionChangeNoteVolume && m_currentNote != NULL )
|
||||
// turn off all notes that are playing
|
||||
const noteVector & notes = m_pattern->notes();
|
||||
|
||||
noteVector::const_iterator it = notes.begin();
|
||||
while( it != notes.end() )
|
||||
{
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOff, 0,
|
||||
m_currentNote->key(), 0 ), midiTime() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
|
||||
midiTime() );
|
||||
if( ( *it )->isPlaying() )
|
||||
{
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOff, 0, ( *it )->key(), 0 ),
|
||||
midiTime() );
|
||||
( *it )->setIsPlaying( false );
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1566,8 +1564,7 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
// (could be the user just moved the cursor one pixel up/down
|
||||
// but is still on the same key)
|
||||
if( key_num != m_lastKey &&
|
||||
m_action != ActionChangeNoteVolume &&
|
||||
m_action != ActionMoveSelection &&
|
||||
( m_action == ActionMoveNote || m_action == ActionMoveSelection ) &&
|
||||
edit_note == false &&
|
||||
_me->buttons() & Qt::LeftButton )
|
||||
{
|
||||
@@ -1606,96 +1603,66 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
{
|
||||
// Volume Bars
|
||||
|
||||
// Use nearest-note when changing volume so the bars can
|
||||
// be "scribbled"
|
||||
int pos_ticks = ( x * midiTime::ticksPerTact() ) /
|
||||
m_ppt + m_currentPosition;
|
||||
// Change notes within a certain pixel range of where
|
||||
// the mouse cursor is
|
||||
int pixel_range = 20;
|
||||
|
||||
// convert to ticks so that we can check which notes
|
||||
// are in the range
|
||||
int ticks_start = (x-pixel_range/2) *
|
||||
midiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
int ticks_end = (x+pixel_range/2) *
|
||||
midiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
|
||||
// get note-vector of current pattern
|
||||
const noteVector & notes = m_pattern->notes();
|
||||
|
||||
note * shortNote = NULL;
|
||||
|
||||
// Max "snap length" 1/8 note on either side
|
||||
int shortDistance = DefaultTicksPerTact/8;
|
||||
|
||||
// loop through vector to find nearest note
|
||||
noteVector::const_iterator it = notes.begin();
|
||||
while( it != notes.end() )
|
||||
{
|
||||
int tmp = abs( pos_ticks - (int)( (*it)->pos() ) );
|
||||
|
||||
if( tmp <= shortDistance && (*it)->length().getTicks() > 0 )
|
||||
{
|
||||
shortDistance = tmp;
|
||||
shortNote = *it ;
|
||||
|
||||
}
|
||||
++it;
|
||||
|
||||
}
|
||||
|
||||
// determine what volume to set note to
|
||||
volume vol = tLimit<int>( 2 * ( -_me->y() +
|
||||
height() -
|
||||
PR_BOTTOM_MARGIN ),
|
||||
MinVolume,
|
||||
MaxVolume );
|
||||
|
||||
if( shortNote )
|
||||
|
||||
// loop through vector
|
||||
bool use_selection = isSelection();
|
||||
noteVector::const_iterator it = notes.begin();
|
||||
while( it != notes.end() )
|
||||
{
|
||||
// have short length - now check for volume difference and currentNote
|
||||
it = notes.begin();
|
||||
|
||||
int shortNotePos = shortNote->pos();
|
||||
int shortVolumeDiff = MaxVolume-MinVolume;
|
||||
|
||||
while( it != notes.end() )
|
||||
if( ( *it )->pos().getTicks() >= ticks_start
|
||||
&& ( *it )->pos().getTicks() <= ticks_end
|
||||
&& ( *it )->length().getTicks() > 0
|
||||
&& ( ( *it )->selected() || ! use_selection ) )
|
||||
{
|
||||
if( (*it)->pos() == shortNotePos && (*it)->length().getTicks() > 0 )
|
||||
{
|
||||
if( *it == m_currentNote )
|
||||
{
|
||||
shortNote = m_currentNote;
|
||||
break;
|
||||
}
|
||||
|
||||
int volDiff = abs( vol - (*it)->getVolume() );
|
||||
if( volDiff <= shortVolumeDiff )
|
||||
{
|
||||
shortVolumeDiff = volDiff;
|
||||
shortNote = *it;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if( shortNote != m_currentNote &&
|
||||
engine::getSong()->isPlaying() == false )
|
||||
{
|
||||
if( m_currentNote != NULL ) {
|
||||
( *it )->setVolume( vol );
|
||||
m_pattern->dataChanged();
|
||||
|
||||
// play the note so that the user can tell how loud it is
|
||||
testPlayNote( *it );
|
||||
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOff, 0,
|
||||
m_currentNote->key(), 0 ), midiTime() );
|
||||
midiEvent(
|
||||
MidiKeyPressure,
|
||||
0,
|
||||
( *it )->key(),
|
||||
vol * 127 / 100 ),
|
||||
midiTime() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( ( *it )->isPlaying() )
|
||||
{
|
||||
// mouse not over this note, stop playing it.
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOff, 0,
|
||||
( *it )->key(), 0 ), midiTime() );
|
||||
|
||||
( *it )->setIsPlaying( false );
|
||||
}
|
||||
}
|
||||
|
||||
if( shortNote != NULL ) {
|
||||
m_lastKey = shortNote->key();
|
||||
++it;
|
||||
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOn, 0,
|
||||
shortNote->key(), shortNote->getVolume() ), midiTime() );
|
||||
}
|
||||
}
|
||||
m_currentNote = shortNote;
|
||||
|
||||
if( m_currentNote != NULL ) {
|
||||
m_currentNote->setVolume( vol );
|
||||
m_pattern->dataChanged();
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiKeyPressure, 0, m_lastKey,
|
||||
vol * 127 / 100 ),
|
||||
midiTime() );
|
||||
}
|
||||
}
|
||||
else if( _me->buttons() == Qt::NoButton && m_editMode == ModeDraw )
|
||||
|
||||
Reference in New Issue
Block a user