Skip to content

Commit

Permalink
Extend LMMS note range to match MIDI specification (fixes LMMS#1857)
Browse files Browse the repository at this point in the history
  • Loading branch information
he29-net committed Dec 17, 2019
1 parent 51b0178 commit 8232e5b
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 124 deletions.
4 changes: 2 additions & 2 deletions data/projects/templates/default.mpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<song>
<trackcontainer width="600" x="5" y="5" maximized="0" height="300" visible="1" type="song" minimized="0">
<track muted="0" type="0" name="TripleOscillator" solo="0">
<instrumenttrack pan="0" fxch="0" usemasterpitch="1" pitchrange="1" pitch="0" basenote="57" vol="100">
<instrumenttrack pan="0" fxch="0" usemasterpitch="1" pitchrange="1" pitch="0" basenote="69" vol="100">
<instrument name="tripleoscillator">
<tripleoscillator phoffset2="0" userwavefile0="" finer0="0" userwavefile1="" finer1="0" userwavefile2="" finer2="0" coarse0="0" coarse1="-12" coarse2="-24" finel0="0" finel1="0" modalgo1="2" modalgo2="2" finel2="0" pan0="0" modalgo3="2" pan1="0" stphdetun0="0" pan2="0" stphdetun1="0" wavetype0="0" stphdetun2="0" wavetype1="0" wavetype2="0" vol0="33" vol1="33" phoffset0="0" phoffset1="0" vol2="33"/>
</instrument>
Expand All @@ -29,7 +29,7 @@
<bbtrack>
<trackcontainer width="640" x="610" y="5" maximized="0" height="400" visible="0" type="bbtrackcontainer" minimized="0">
<track muted="0" type="0" name="Kicker" solo="0">
<instrumenttrack pan="0" fxch="0" usemasterpitch="1" pitchrange="1" pitch="0" basenote="57" vol="100">
<instrumenttrack pan="0" fxch="0" usemasterpitch="1" pitchrange="1" pitch="0" basenote="69" vol="100">
<instrument name="kicker">
<kicker decay_numerator="4" decay_denominator="4" distend="0.8" click="0.4" endnote="0" version="1" decay_syncmode="0" decay="440" noise="0" slope="0.06" dist="0.8" env="0.163" startnote="1" startfreq="150" endfreq="40" gain="1"/>
</instrument>
Expand Down
10 changes: 6 additions & 4 deletions include/Note.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum Keys

enum Octaves
{
Octave_m1, // MIDI standard starts at C-1
Octave_0,
Octave_1,
Octave_2,
Expand All @@ -64,15 +65,16 @@ enum Octaves
Octave_6,
Octave_7,
Octave_8,
Octave_9, // incomplete octave, MIDI only goes up to G9
NumOctaves
} ;

};

const int FirstOctave = -1;
const int WhiteKeysPerOctave = 7;
const int BlackKeysPerOctave = 5;
const int KeysPerOctave = WhiteKeysPerOctave + BlackKeysPerOctave;
const int NumKeys = NumOctaves * KeysPerOctave;
const int DefaultKey = DefaultOctave*KeysPerOctave + Key_A;
const int NumKeys = qMin(NumOctaves * KeysPerOctave, 128); // limited to MIDI range
const int DefaultKey = DefaultOctave * KeysPerOctave + Key_A;

const float MaxDetuning = 4 * 12.0f;

Expand Down
2 changes: 1 addition & 1 deletion include/lmms_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const float F_E_R = (float) LD_E_R;

// Frequency ranges (in Hz).
// Arbitrary low limit for logarithmic frequency scale; >1 Hz.
const int LOWEST_LOG_FREQ = 10;
const int LOWEST_LOG_FREQ = 5;

// Full range is defined by LOWEST_LOG_FREQ and current sample rate.
enum FREQUENCY_RANGES
Expand Down
6 changes: 2 additions & 4 deletions plugins/MidiExport/MidiExport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks,
if (n.nodeName() == "instrumenttrack")
{
QDomElement it = n.toElement();
// transpose +12 semitones, workaround for #1857
base_pitch = (69 - it.attribute("basenote", "57").toInt());
base_pitch = (69 - it.attribute("basenote", "69").toInt());
if (it.attribute("usemasterpitch", "1").toInt())
{
base_pitch += masterPitch;
Expand Down Expand Up @@ -200,8 +199,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks,
if (n.nodeName() == "instrumenttrack")
{
QDomElement it = n.toElement();
// transpose +12 semitones, workaround for #1857
base_pitch = (69 - it.attribute("basenote", "57").toInt());
base_pitch = (69 - it.attribute("basenote", "69").toInt());
if (it.attribute("usemasterpitch", "1").toInt())
{
base_pitch += masterPitch;
Expand Down
2 changes: 1 addition & 1 deletion plugins/MidiImport/MidiImport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ bool MidiImport::readSMF( TrackContainer* tc )
int ticks = noteEvt->get_duration() * ticksPerBeat;
Note n( (ticks < 1 ? 1 : ticks ),
noteEvt->get_start_time() * ticksPerBeat,
noteEvt->get_identifier() - 12,
noteEvt->get_identifier(),
noteEvt->get_loud() * (200.f / 127.f)); // Map from MIDI velocity to LMMS volume
ch->addNote( n );

Expand Down
15 changes: 6 additions & 9 deletions src/core/midi/MidiAlsaSeq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,21 +176,21 @@ void MidiAlsaSeq::processOutEvent( const MidiEvent& event, const MidiTime& time,
case MidiNoteOn:
snd_seq_ev_set_noteon( &ev,
event.channel(),
event.key() + KeysPerOctave,
event.key(),
event.velocity() );
break;

case MidiNoteOff:
snd_seq_ev_set_noteoff( &ev,
event.channel(),
event.key() + KeysPerOctave,
event.key(),
event.velocity() );
break;

case MidiKeyPressure:
snd_seq_ev_set_keypress( &ev,
event.channel(),
event.key() + KeysPerOctave,
event.key(),
event.velocity() );
break;

Expand Down Expand Up @@ -531,8 +531,7 @@ void MidiAlsaSeq::run()
case SND_SEQ_EVENT_NOTEON:
dest->processInEvent( MidiEvent( MidiNoteOn,
ev->data.note.channel,
ev->data.note.note -
KeysPerOctave,
ev->data.note.note,
ev->data.note.velocity,
source
),
Expand All @@ -542,8 +541,7 @@ void MidiAlsaSeq::run()
case SND_SEQ_EVENT_NOTEOFF:
dest->processInEvent( MidiEvent( MidiNoteOff,
ev->data.note.channel,
ev->data.note.note -
KeysPerOctave,
ev->data.note.note,
ev->data.note.velocity,
source
),
Expand All @@ -554,8 +552,7 @@ void MidiAlsaSeq::run()
dest->processInEvent( MidiEvent(
MidiKeyPressure,
ev->data.note.channel,
ev->data.note.note -
KeysPerOctave,
ev->data.note.note,
ev->data.note.velocity,
source
), MidiTime() );
Expand Down
26 changes: 11 additions & 15 deletions src/core/midi/MidiApple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,28 +318,24 @@ void MidiApple::HandleReadCallback( const MIDIPacketList *pktlist, void *srcConn
}

unsigned char messageChannel = status & 0xF;
const MidiEventTypes cmdtype = static_cast<MidiEventTypes>( status & 0xF0 );
const MidiEventTypes cmdtype = static_cast<MidiEventTypes>(status & 0xF0);
const int par1 = packet->data[iByte + 1];
const int par2 = packet->data[iByte + 2];

switch (cmdtype)
{
case MidiNoteOff: //0x80:
case MidiNoteOn: //0x90:
case MidiKeyPressure: //0xA0:
notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1 - KeysPerOctave, par2 & 0xff, &endPointRef ));
case MidiNoteOff: //0x80:
case MidiNoteOn: //0x90:
case MidiKeyPressure: //0xA0:
case MidiControlChange: //0xB0:
case MidiProgramChange: //0xC0:
case MidiChannelPressure: //0xD0:
notifyMidiPortList(m_inputSubs[refName], MidiEvent(cmdtype, messageChannel, par1, par2 & 0xff, &endPointRef));
break;

case MidiControlChange: //0xB0:
case MidiProgramChange: //0xC0:
case MidiChannelPressure: //0xD0:
notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1, par2 & 0xff, &endPointRef ));
case MidiPitchBend: //0xE0:
notifyMidiPortList(m_inputSubs[refName], MidiEvent(cmdtype, messageChannel, par1 + par2 * 128, 0, &endPointRef));
break;

case MidiPitchBend: //0xE0:
notifyMidiPortList(m_inputSubs[refName],MidiEvent( cmdtype, messageChannel, par1 + par2 * 128, 0, &endPointRef ));
break;
case MidiActiveSensing: //0xF0
case MidiActiveSensing: //0xF0
case 0xF0:
break;
default:
Expand Down
33 changes: 14 additions & 19 deletions src/core/midi/MidiClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,34 +214,30 @@ void MidiClientRaw::parseData( const unsigned char c )
* We simply keep the status as it is, just reset the parameter counter.
* If another status byte comes in, it will overwrite the status.
*/
m_midiParseData.m_midiEvent.setType( static_cast<MidiEventTypes>( m_midiParseData.m_status ) );
m_midiParseData.m_midiEvent.setChannel( m_midiParseData.m_channel );
m_midiParseData.m_midiEvent.setType(static_cast<MidiEventTypes>(m_midiParseData.m_status));
m_midiParseData.m_midiEvent.setChannel(m_midiParseData.m_channel);
m_midiParseData.m_bytes = 0; /* Related to running status! */
switch( m_midiParseData.m_midiEvent.type() )
switch(m_midiParseData.m_midiEvent.type())
{
case MidiNoteOff:
case MidiNoteOn:
case MidiKeyPressure:
case MidiChannelPressure:
m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] - KeysPerOctave );
m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] );
break;

case MidiProgramChange:
m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] );
m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] );
m_midiParseData.m_midiEvent.setKey(m_midiParseData.m_buffer[0]);
m_midiParseData.m_midiEvent.setVelocity(m_midiParseData.m_buffer[1]);
break;

case MidiControlChange:
m_midiParseData.m_midiEvent.setControllerNumber( m_midiParseData.m_buffer[0] );
m_midiParseData.m_midiEvent.setControllerValue( m_midiParseData.m_buffer[1] );
m_midiParseData.m_midiEvent.setControllerNumber(m_midiParseData.m_buffer[0]);
m_midiParseData.m_midiEvent.setControllerValue( m_midiParseData.m_buffer[1]);
break;

case MidiPitchBend:
// Pitch-bend is transmitted with 14-bit precision.
// Note: '|' does here the same as '+' (no common bits),
// but might be faster
m_midiParseData.m_midiEvent.setPitchBend( ( m_midiParseData.m_buffer[1] * 128 ) | m_midiParseData.m_buffer[0] );
m_midiParseData.m_midiEvent.setPitchBend((m_midiParseData.m_buffer[1] * 128) | m_midiParseData.m_buffer[0]);
break;

default:
Expand All @@ -266,22 +262,21 @@ void MidiClientRaw::processParsedEvent()



void MidiClientRaw::processOutEvent( const MidiEvent& event, const MidiTime & , const MidiPort* port )
void MidiClientRaw::processOutEvent(const MidiEvent& event, const MidiTime &, const MidiPort* port)
{
// TODO: also evaluate _time and queue event if necessary
switch( event.type() )
switch(event.type())
{
case MidiNoteOn:
case MidiNoteOff:
case MidiKeyPressure:
sendByte( event.type() | event.channel() );
sendByte( event.key() + KeysPerOctave );
sendByte( event.velocity() );
sendByte(event.type() | event.channel());
sendByte(event.key());
sendByte(event.velocity());
break;

default:
qWarning( "MidiClientRaw: unhandled MIDI-event %d\n",
(int) event.type() );
qWarning("MidiClientRaw: unhandled MIDI-event %d\n", (int)event.type());
break;
}
}
Expand Down
13 changes: 5 additions & 8 deletions src/core/midi/MidiWinMM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,28 +201,25 @@ void MidiWinMM::handleInputEvent( HMIDIIN hm, DWORD ev )
}

const MidiPortList & l = m_inputSubs[d];
for( MidiPortList::ConstIterator it = l.begin(); it != l.end(); ++it )
for (MidiPortList::ConstIterator it = l.begin(); it != l.end(); ++it)
{
switch( cmdtype )
switch(cmdtype)
{
case MidiNoteOn:
case MidiNoteOff:
case MidiKeyPressure:
( *it )->processInEvent( MidiEvent( cmdtype, chan, par1 - KeysPerOctave, par2 & 0xff, &hm ) );
break;

case MidiControlChange:
case MidiProgramChange:
case MidiChannelPressure:
( *it )->processInEvent( MidiEvent( cmdtype, chan, par1, par2 & 0xff, &hm ) );
(*it)->processInEvent(MidiEvent(cmdtype, chan, par1, par2 & 0xff, &hm));
break;

case MidiPitchBend:
( *it )->processInEvent( MidiEvent( cmdtype, chan, par1 + par2*128, 0, &hm ) );
(*it)->processInEvent(MidiEvent(cmdtype, chan, par1 + par2 * 128, 0, &hm));
break;

default:
qWarning( "MidiWinMM: unhandled input event %d\n", cmdtype );
qWarning("MidiWinMM: unhandled input event %d\n", cmdtype);
break;
}
}
Expand Down
42 changes: 20 additions & 22 deletions src/gui/PianoView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ PianoView::PianoView( QWidget * _parent ) :
s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( "black_key_pressed" ) );
}

setAttribute( Qt::WA_OpaquePaintEvent, true );
setFocusPolicy( Qt::StrongFocus );
setMaximumWidth( WhiteKeysPerOctave * NumOctaves * PW_WHITE_KEY_WIDTH );
setAttribute(Qt::WA_OpaquePaintEvent, true);
setFocusPolicy(Qt::StrongFocus);
setMaximumWidth(WhiteKeysPerOctave * NumKeys * PW_WHITE_KEY_WIDTH / KeysPerOctave);

// create scrollbar at the bottom
m_pianoScroll = new QScrollBar( Qt::Horizontal, this );
Expand Down Expand Up @@ -684,14 +684,13 @@ void PianoView::focusOutEvent( QFocusEvent * )
* After resizing we need to adjust range of scrollbar for not allowing
* to scroll too far to the right.
*
* \param _event resize-event object (unused)
* \param event resize-event object (unused)
*/
void PianoView::resizeEvent( QResizeEvent * _event )
void PianoView::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent( _event );
m_pianoScroll->setRange( 0, WhiteKeysPerOctave * NumOctaves -
(int) ceil( (float) width() /
PW_WHITE_KEY_WIDTH ) );
QWidget::resizeEvent(event);
m_pianoScroll->setRange(0, WhiteKeysPerOctave * (NumKeys + 1) / KeysPerOctave -
(int)ceil((float)width() / PW_WHITE_KEY_WIDTH));
}


Expand Down Expand Up @@ -819,13 +818,11 @@ void PianoView::paintEvent( QPaintEvent * )

x += PW_WHITE_KEY_WIDTH;

if( (Keys) (cur_key%KeysPerOctave) == Key_C )
if ((Keys)(cur_key % KeysPerOctave) == Key_C)
{
// label key of note C with "C" and number of current
// octave
p.drawText( x - PW_WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2,
QString( "C" ) + QString::number(
cur_key / KeysPerOctave, 10 ) );
// label key of note C with "C" and number of current octave
p.drawText(x - PW_WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2,
QString("C") + QString::number(FirstOctave + cur_key / KeysPerOctave));
}
++cur_key;
}
Expand All @@ -849,19 +846,19 @@ void PianoView::paintEvent( QPaintEvent * )
}

// now draw all black keys...
for( int x = 0; x < width(); )
for (int x = 0; x < width();)
{
if( Piano::isBlackKey( cur_key ) )
if (Piano::isBlackKey(cur_key))
{
// draw pressed or not pressed key, depending on
// state of current key
if( m_piano && m_piano->isKeyPressed( cur_key ) )
if (m_piano && m_piano->isKeyPressed(cur_key))
{
p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm );
p.drawPixmap(x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm);
}
else
{
p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm );
p.drawPixmap(x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm);
}
x += PW_WHITE_KEY_WIDTH;
white_cnt = 0;
Expand All @@ -871,12 +868,13 @@ void PianoView::paintEvent( QPaintEvent * )
// simple workaround for increasing x if there were two
// white keys (e.g. between E and F)
++white_cnt;
if( white_cnt > 1 )
if (white_cnt > 1)
{
x += PW_WHITE_KEY_WIDTH;
}
}
++cur_key;
// stop drawing when all keys are drawn, even if an extra black key could fit
if (++cur_key == NumKeys) {break;}
}
}

Expand Down
Loading

0 comments on commit 8232e5b

Please sign in to comment.