Note panning editing support in the piano roll
git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1918 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
21
ChangeLog
21
ChangeLog
@@ -31,6 +31,24 @@
|
||||
- added support for MidiMetaEvents to midiEvent
|
||||
- added MidiMetaPanning event
|
||||
|
||||
2008-12-13 Andrew Kelley <superjoe30/at/gmail/dot/com>
|
||||
|
||||
* include/piano_roll.h:
|
||||
* src/gui/piano_roll.cpp:
|
||||
* src/tracks/instrument_track.cpp:
|
||||
* TODO:
|
||||
- first draft of panning editing. (for now, click the area below
|
||||
the piano to switch editing modes.)
|
||||
- supports horizontal mouse wheel
|
||||
|
||||
* include/panning_constants.h:
|
||||
* include/midi.h:
|
||||
* include/panning.h:
|
||||
- separated panning constants from panningToVolumeVector so that midi.h
|
||||
could have access to PanningLeft, PanningRight, etc
|
||||
- added MidiNotePanning meta midi instruction to allow on the fly
|
||||
panning changes to instrument notes
|
||||
|
||||
2008-12-12 Andrew Kelley <superjoe30/at/gmail/dot/com>
|
||||
|
||||
* include/note.h:
|
||||
@@ -42,6 +60,9 @@
|
||||
is implemented more cleanly
|
||||
- editing note volume only affects selected notes (or all notes if
|
||||
none selected)
|
||||
- clicking a note volume no longer affects the nearest volume handle.
|
||||
Instead, select the note(s) whose volumes you want to change and then
|
||||
scribble their volumes.
|
||||
- when "scribbling" note volumes, if there is a chord, it will play all
|
||||
3 notes, rather than just the first one
|
||||
- if you click towards the end of the key in the piano roll, it plays
|
||||
|
||||
6
TODO
6
TODO
@@ -52,7 +52,6 @@
|
||||
- add FLAC as export-format?
|
||||
|
||||
Andrew Kelley's todo:
|
||||
- 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
|
||||
@@ -63,6 +62,10 @@ Andrew Kelley's todo:
|
||||
* quick slice
|
||||
* look through FL Studio's tools and implement some of them
|
||||
- if you press both controls at the same time, the piano roll gets stuck in selection mode
|
||||
- you can still move volumes for notes if you click down right on it, even if the
|
||||
note is not selected.
|
||||
- change my modifier code to use mainwindows modifier info
|
||||
- fix ctrl+mousewheel zooming in
|
||||
|
||||
- recording automation
|
||||
- make knobs easier to tune (less sensitive)
|
||||
@@ -78,5 +81,4 @@ Andrew Kelley's todo:
|
||||
- the 'add beat+bassline' button in the beat+bassline editor is misleading - I say we remove it and rely on the song editor to add beat+basslines
|
||||
- make it so that 3xosc notes don't max out
|
||||
- implement note detuning (used to be ctrl+click to access note detuning) (need some other intuitive way to access note detuning as ctrl, shift, and alt are all being used)
|
||||
- make the horizontal mouse wheel do the same thing as shift+vertical mouse wheel
|
||||
- copy-pasted automation patterns have to be manually linked back to their knob for some reason
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
#ifndef _MIDI_H
|
||||
#define _MIDI_H
|
||||
|
||||
|
||||
#include "lmms_basics.h"
|
||||
#include "panning_constants.h"
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ const int MidiControllerCount = 128;
|
||||
const int MidiProgramCount = 128;
|
||||
const int MidiMaxVelocity = 127;
|
||||
|
||||
const int MidiMaxPanning = 127;
|
||||
const int MidiMinPanning = -128;
|
||||
|
||||
|
||||
struct midiEvent
|
||||
{
|
||||
@@ -145,11 +148,24 @@ struct midiEvent
|
||||
{
|
||||
return m_data.m_param[1];
|
||||
}
|
||||
|
||||
inline Sint16 midiPanning( void ) const
|
||||
{
|
||||
return m_data.m_param[1];
|
||||
}
|
||||
|
||||
inline volume getVolume( void ) const
|
||||
{
|
||||
return (volume)( velocity() * 100 / MidiMaxVelocity );
|
||||
}
|
||||
|
||||
inline panning getPanning( void ) const
|
||||
{
|
||||
return (panning) ( PanningLeft +
|
||||
( (float)( midiPanning() - MidiMinPanning ) ) /
|
||||
( (float)( MidiMaxPanning - MidiMinPanning ) ) *
|
||||
( (float)( PanningRight - PanningLeft ) ) );
|
||||
}
|
||||
|
||||
|
||||
MidiEventTypes m_type; // MIDI event type
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* panning.h - declaration of some constants and types, concerning the
|
||||
* panning.h - declaration of some types, concerning the
|
||||
* panning of a note
|
||||
*
|
||||
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
@@ -29,11 +29,7 @@
|
||||
#include "lmms_basics.h"
|
||||
#include "volume.h"
|
||||
#include "templates.h"
|
||||
|
||||
const panning PanningRight = ( 0 + 100 );
|
||||
const panning PanningLeft = - PanningRight;
|
||||
const panning PanningCenter = 0;
|
||||
const panning DefaultPanning = PanningCenter;
|
||||
#include "panning_constants.h"
|
||||
|
||||
inline stereoVolumeVector panningToVolumeVector( panning _p,
|
||||
float _scale = 1.0f )
|
||||
|
||||
34
include/panning_constants.h
Normal file
34
include/panning_constants.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* panning_constants.h - declaration of some constants, concerning the
|
||||
* panning of a note
|
||||
*
|
||||
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PANNING_CONSTANTS_H
|
||||
#define _PANNING_CONSTANTS_H
|
||||
|
||||
const panning PanningRight = ( 0 + 100 );
|
||||
const panning PanningLeft = - PanningRight;
|
||||
const panning PanningCenter = 0;
|
||||
const panning DefaultPanning = PanningCenter;
|
||||
|
||||
#endif
|
||||
@@ -139,7 +139,7 @@ private:
|
||||
ModeSelect,
|
||||
ModeMove,
|
||||
ModeOpen
|
||||
} ;
|
||||
};
|
||||
|
||||
enum actions
|
||||
{
|
||||
@@ -147,18 +147,23 @@ private:
|
||||
ActionMoveNote,
|
||||
ActionResizeNote,
|
||||
ActionSelectNotes,
|
||||
ActionMoveSelection,
|
||||
ActionChangeNoteVolume,
|
||||
ActionChangeNotePanning,
|
||||
ActionResizeNoteEditArea,
|
||||
} ;
|
||||
ActionChangeNoteProperty,
|
||||
ActionResizeNoteEditArea
|
||||
};
|
||||
|
||||
enum noteEditMode
|
||||
{
|
||||
NoteEditVolume,
|
||||
NoteEditPanning,
|
||||
NoteEditCount
|
||||
};
|
||||
|
||||
enum pianoRollKeyTypes
|
||||
{
|
||||
PR_WHITE_KEY_SMALL,
|
||||
PR_WHITE_KEY_BIG,
|
||||
PR_BLACK_KEY
|
||||
} ;
|
||||
};
|
||||
|
||||
|
||||
pianoRoll( void );
|
||||
@@ -230,6 +235,7 @@ private:
|
||||
|
||||
note * m_currentNote;
|
||||
actions m_action;
|
||||
noteEditMode m_noteEditMode;
|
||||
|
||||
Uint32 m_selectStartTick;
|
||||
int m_selectedTick;
|
||||
|
||||
@@ -144,6 +144,7 @@ pianoRoll::pianoRoll( void ) :
|
||||
m_recording( false ),
|
||||
m_currentNote( NULL ),
|
||||
m_action( ActionNone ),
|
||||
m_noteEditMode( NoteEditVolume ),
|
||||
m_lastMouseX( 0 ),
|
||||
m_lastMouseY( 0 ),
|
||||
m_oldNotesEditHeight( 100 ),
|
||||
@@ -1144,8 +1145,6 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
|
||||
if( _me->y() > PR_TOP_MARGIN )
|
||||
{
|
||||
volume vol = DefaultVolume;
|
||||
|
||||
bool edit_note = ( _me->y() > noteEditTop() );
|
||||
|
||||
int key_num = getKey( _me->y() );
|
||||
@@ -1204,36 +1203,9 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
// area
|
||||
if( edit_note == true )
|
||||
{
|
||||
if( it != notes.end() )
|
||||
{
|
||||
vol = 2 * ( -_me->y() + height() -
|
||||
PR_BOTTOM_MARGIN );
|
||||
|
||||
int shortVolumeDiff = MaxVolume - MinVolume;
|
||||
noteVector::const_iterator jt = notes.begin();
|
||||
while( jt != notes.end() )
|
||||
{
|
||||
if( (*jt)->pos() == (*it)->pos() && (*jt)->length().getTicks() > 0 )
|
||||
{
|
||||
|
||||
int volDiff = abs( vol - (*jt)->getVolume() );
|
||||
if( volDiff <= shortVolumeDiff )
|
||||
{
|
||||
shortVolumeDiff = volDiff;
|
||||
it = jt;
|
||||
}
|
||||
}
|
||||
++jt;
|
||||
}
|
||||
|
||||
( *it )->setVolume( vol );
|
||||
m_currentNote = *it;
|
||||
m_action = ActionChangeNoteVolume;
|
||||
key_num = ( *it )->key();
|
||||
|
||||
testPlayNote( *it );
|
||||
|
||||
}
|
||||
// scribble note edit changes
|
||||
mouseMoveEvent( _me );
|
||||
return;
|
||||
}
|
||||
// left button??
|
||||
else if( _me->button() == Qt::LeftButton &&
|
||||
@@ -1434,7 +1406,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
|
||||
update();
|
||||
}
|
||||
else
|
||||
else if( _me->y() < keyAreaBottom() )
|
||||
{
|
||||
// clicked on keyboard on the left - play note
|
||||
m_lastKey = key_num;
|
||||
@@ -1446,6 +1418,16 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
midiTime() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// clicked in the box below the keys to the left of note edit area
|
||||
m_noteEditMode = (noteEditMode)(((int)m_noteEditMode)+1);
|
||||
if( m_noteEditMode > 1 )
|
||||
{
|
||||
m_noteEditMode = (noteEditMode)0;
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1662,11 +1644,10 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
// is the calculated key different from current key?
|
||||
// (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 == ActionMoveNote || m_action == ActionMoveSelection )
|
||||
|| ( x < WHITE_KEY_WIDTH && m_action == ActionNone ) ) &&
|
||||
edit_note == false &&
|
||||
_me->buttons() & Qt::LeftButton )
|
||||
if( key_num != m_lastKey && edit_note == false &&
|
||||
_me->buttons() & Qt::LeftButton &&
|
||||
( m_action == ActionMoveNote ||
|
||||
( x < WHITE_KEY_WIDTH && m_action == ActionNone ) ) )
|
||||
{
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
|
||||
@@ -1699,10 +1680,10 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
// handle moving notes and resizing them
|
||||
dragNotes(_me->x(), _me->y(), _me->modifiers() & Qt::AltModifier);
|
||||
}
|
||||
else if( ( edit_note == true || m_action == ActionChangeNoteVolume ) &&
|
||||
else if( ( edit_note == true || m_action == ActionChangeNoteProperty ) &&
|
||||
_me->buttons() & Qt::LeftButton )
|
||||
{
|
||||
// Volume Bars
|
||||
// editing note properties
|
||||
|
||||
// Change notes within a certain pixel range of where
|
||||
// the mouse cursor is
|
||||
@@ -1718,12 +1699,17 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
// get note-vector of current pattern
|
||||
const noteVector & notes = m_pattern->notes();
|
||||
|
||||
// determine what volume to set note to
|
||||
volume vol = tLimit<int>(
|
||||
// determine what volume/panning to set note to
|
||||
volume vol = tLimit<int>( MinVolume +
|
||||
( ( (float)noteEditBottom() ) - ( (float)_me->y() ) ) /
|
||||
( (float)( noteEditBottom() - noteEditTop() ) ) *
|
||||
( MaxVolume - MinVolume ),
|
||||
MinVolume, MaxVolume );
|
||||
panning pan = tLimit<int>( PanningLeft +
|
||||
( (float)( noteEditBottom() - _me->y() ) ) /
|
||||
( (float)( noteEditBottom() - noteEditTop() ) ) *
|
||||
( (float)( PanningRight - PanningLeft ) ),
|
||||
PanningLeft, PanningRight);
|
||||
|
||||
// loop through vector
|
||||
bool use_selection = isSelection();
|
||||
@@ -1735,19 +1721,35 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
&& ( *it )->length().getTicks() > 0
|
||||
&& ( ( *it )->selected() || ! use_selection ) )
|
||||
{
|
||||
( *it )->setVolume( vol );
|
||||
m_pattern->dataChanged();
|
||||
|
||||
// play the note so that the user can tell how loud it is
|
||||
// and where it is panned
|
||||
testPlayNote( *it );
|
||||
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent(
|
||||
MidiKeyPressure,
|
||||
0,
|
||||
( *it )->key(),
|
||||
vol * 127 / 100 ),
|
||||
midiTime() );
|
||||
if( m_noteEditMode == NoteEditVolume )
|
||||
{
|
||||
( *it )->setVolume( vol );
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
midiEvent(
|
||||
MidiKeyPressure,
|
||||
0,
|
||||
( *it )->key(),
|
||||
vol * 127 / 100),
|
||||
midiTime() );
|
||||
}
|
||||
else if( m_noteEditMode == NoteEditPanning )
|
||||
{
|
||||
( *it )->setPanning( pan );
|
||||
midiEvent evt( MidiMetaEvent, 0, ( *it )->key(),
|
||||
MidiMinPanning +
|
||||
( (float)( pan - PanningLeft ) ) /
|
||||
( (float)( PanningRight - PanningLeft ) ) *
|
||||
( (float)( MidiMaxPanning - MidiMinPanning ) ) );
|
||||
evt.m_metaEvent = MidiNotePanning;
|
||||
m_pattern->getInstrumentTrack()->processInEvent(
|
||||
evt, midiTime() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2381,7 +2383,8 @@ void pianoRoll::paintEvent( QPaintEvent * _pe )
|
||||
|
||||
|
||||
|
||||
// following code draws all notes in visible area + volume-lines
|
||||
// following code draws all notes in visible area
|
||||
// and the note editing stuff (volume, panning, etc)
|
||||
|
||||
// setup selection-vars
|
||||
int sel_pos_start = m_selectStartTick;
|
||||
@@ -2410,7 +2413,7 @@ void pianoRoll::paintEvent( QPaintEvent * _pe )
|
||||
const int visible_keys = ( keyAreaBottom()-keyAreaTop() ) /
|
||||
KEY_LINE_HEIGHT + 2;
|
||||
|
||||
QPolygon volumeHandles;
|
||||
QPolygon editHandles;
|
||||
|
||||
for( noteVector::const_iterator it = notes.begin();
|
||||
it != notes.end(); ++it )
|
||||
@@ -2453,22 +2456,46 @@ void pianoRoll::paintEvent( QPaintEvent * _pe )
|
||||
( *it )->selected(),
|
||||
( *it )->length() < 0 );
|
||||
}
|
||||
// draw volume-line of note
|
||||
QColor color = QColor::fromHsv( 120, 221,
|
||||
qMin(255, 60 + ( *it )->getVolume() ) );
|
||||
p.setPen( QPen( color,
|
||||
NE_LINE_WIDTH ) );
|
||||
|
||||
int volumeHandleTop = noteEditBottom() -
|
||||
( (float)( ( *it )->getVolume() - MinVolume ) ) /
|
||||
( (float)( MaxVolume - MinVolume ) ) *
|
||||
( (float)( noteEditBottom() - noteEditTop() ) );
|
||||
p.drawLine( noteEditLeft() + x, volumeHandleTop,
|
||||
noteEditLeft() + x, noteEditBottom() );
|
||||
// draw note editing stuff
|
||||
int editHandleTop = 0;
|
||||
if( m_noteEditMode == NoteEditVolume )
|
||||
{
|
||||
QColor color = QColor::fromHsv( 120, 221,
|
||||
qMin(255, 60 + ( *it )->getVolume() ) );
|
||||
p.setPen( QPen( color,
|
||||
NE_LINE_WIDTH ) );
|
||||
|
||||
editHandleTop = noteEditBottom() -
|
||||
( (float)( ( *it )->getVolume() - MinVolume ) ) /
|
||||
( (float)( MaxVolume - MinVolume ) ) *
|
||||
( (float)( noteEditBottom() - noteEditTop() ) );
|
||||
|
||||
p.drawLine( noteEditLeft() + x, editHandleTop,
|
||||
noteEditLeft() + x, noteEditBottom() );
|
||||
|
||||
|
||||
volumeHandles << QPoint( x + noteEditLeft() + 1,
|
||||
volumeHandleTop+1 );
|
||||
}
|
||||
else if( m_noteEditMode == NoteEditPanning )
|
||||
{
|
||||
QColor color( 0xFF, 0xB0, 0x00 );
|
||||
if( ( *it )->selected() )
|
||||
{
|
||||
color.setRgb( 0x00, 0x40, 0xC0 );
|
||||
}
|
||||
|
||||
p.setPen( QPen( color, NE_LINE_WIDTH ) );
|
||||
|
||||
editHandleTop = noteEditBottom() -
|
||||
( (float)( ( *it )->getPanning() - PanningLeft ) ) /
|
||||
( (float)( (PanningRight - PanningLeft ) ) ) *
|
||||
( (float)( noteEditBottom() - noteEditTop() ) );
|
||||
|
||||
p.drawLine( noteEditLeft() + x, noteEditTop() +
|
||||
( (float)( noteEditBottom() - noteEditTop() ) ) / 2.0f,
|
||||
noteEditLeft() + x, editHandleTop );
|
||||
}
|
||||
editHandles << QPoint( x + noteEditLeft() + 1,
|
||||
editHandleTop+1 );
|
||||
|
||||
if( ( *it )->hasDetuningInfo() )
|
||||
{
|
||||
@@ -2480,7 +2507,7 @@ void pianoRoll::paintEvent( QPaintEvent * _pe )
|
||||
|
||||
p.setPen( QPen( QColor( 0xEA, 0xA1, 0x00 ),
|
||||
NE_LINE_WIDTH*2 ) );
|
||||
p.drawPoints( volumeHandles );
|
||||
p.drawPoints( editHandles );
|
||||
|
||||
}
|
||||
else
|
||||
@@ -2543,8 +2570,7 @@ void pianoRoll::paintEvent( QPaintEvent * _pe )
|
||||
{
|
||||
cursor = s_toolErase;
|
||||
}
|
||||
else if( m_action == ActionMoveSelection ||
|
||||
m_action == ActionMoveNote )
|
||||
else if( m_action == ActionMoveNote )
|
||||
{
|
||||
cursor = s_toolMove;
|
||||
}
|
||||
@@ -2622,7 +2648,8 @@ void pianoRoll::wheelEvent( QWheelEvent * _we )
|
||||
m_timeLine->setPixelsPerTact( m_ppt );
|
||||
update();
|
||||
}
|
||||
else if( engine::getMainWindow()->isShiftPressed() )
|
||||
else if( engine::getMainWindow()->isShiftPressed()
|
||||
|| _we->orientation() == Qt::Horizontal )
|
||||
{
|
||||
m_leftRightScroll->setValue( m_leftRightScroll->value() -
|
||||
_we->delta() * 2 / 15 );
|
||||
|
||||
@@ -176,13 +176,12 @@ void instrumentTrack::processAudioBuffer( sampleFrame * _buf,
|
||||
}
|
||||
|
||||
m_audioPort.setNextFxChannel( m_effectChannelModel.value() );
|
||||
engine::getMixer()->bufferToPort( _buf,
|
||||
( _n != NULL ) ? qMin<f_cnt_t>(
|
||||
_n->framesLeftForCurrentPeriod(), _frames ) :
|
||||
engine::getMixer()->bufferToPort( _buf, ( _n != NULL ) ? qMin<f_cnt_t>(_n->framesLeftForCurrentPeriod(), _frames ) :
|
||||
_frames,
|
||||
( _n != NULL ) ? _n->offset() : 0,
|
||||
panningToVolumeVector( (int) m_panningModel.value(),
|
||||
v_scale ),
|
||||
panningToVolumeVector(
|
||||
(int) m_panningModel.value() + _n->getPanning(),
|
||||
v_scale ),
|
||||
&m_audioPort );
|
||||
}
|
||||
|
||||
@@ -296,7 +295,24 @@ void instrumentTrack::processInEvent( const midiEvent & _me,
|
||||
case MidiProgramChange:
|
||||
m_instrument->handleMidiEvent( _me, _time );
|
||||
break;
|
||||
|
||||
|
||||
case MidiMetaEvent:
|
||||
// handle special cases such as note panning
|
||||
switch( _me.m_metaEvent )
|
||||
{
|
||||
case MidiNotePanning:
|
||||
if( m_notes[_me.key()] != NULL )
|
||||
{
|
||||
m_notes[_me.key()]->setPanning( _me.getPanning() );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf( "instrument-track: unhandled "
|
||||
"MIDI meta event: %i\n", _me.m_metaEvent );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if( !m_instrument->handleMidiEvent( _me, _time ) )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user