Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend LMMS note range to match MIDI specification #5349

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inserting this before Octave_0 could cause problems if someone didn't use the enum. Will need to check this as best we can.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to go around and grep for the instances of word "Octave" and the number 57 (previous base note) to correct all the code affected by my changes, but this is my first time touching the core (apart from fft_helpers), so I definitely could have missed something. I'm not sure what would be a good, comprehensive test for this, though.

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