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 53b31ca commit 558ef8b
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 122 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
43 changes: 43 additions & 0 deletions include/lmms_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,47 @@ const float F_PI_SQR = (float) LD_PI_SQR;
const float F_E = (float) LD_E;
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 = 5;

// Full range is defined by LOWEST_LOG_FREQ and current sample rate.
enum FREQUENCY_RANGES
{
FRANGE_FULL = 0,
FRANGE_AUDIBLE,
FRANGE_BASS,
FRANGE_MIDS,
FRANGE_HIGH
};

const int FRANGE_AUDIBLE_START = 20;
const int FRANGE_AUDIBLE_END = 20000;
const int FRANGE_BASS_START = 20;
const int FRANGE_BASS_END = 300;
const int FRANGE_MIDS_START = 200;
const int FRANGE_MIDS_END = 5000;
const int FRANGE_HIGH_START = 4000;
const int FRANGE_HIGH_END = 20000;

// Amplitude ranges (in dBFS).
// Reference: full scale sine wave (-1.0 to 1.0) is 0 dB.
// Doubling or halving the amplitude produces 3 dB difference.
enum AMPLITUDE_RANGES
{
ARANGE_EXTENDED = 0,
ARANGE_AUDIBLE,
ARANGE_LOUD,
ARANGE_SILENT
};

const int ARANGE_EXTENDED_START = -80;
const int ARANGE_EXTENDED_END = 20;
const int ARANGE_AUDIBLE_START = -50;
const int ARANGE_AUDIBLE_END = 0;
const int ARANGE_LOUD_START = -30;
const int ARANGE_LOUD_END = 0;
const int ARANGE_SILENT_START = -60;
const int ARANGE_SILENT_END = -10;

#endif
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 @@ -427,7 +427,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
31 changes: 15 additions & 16 deletions src/core/midi/MidiClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,30 +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 MidiProgramChange:
case MidiChannelPressure:
m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] - KeysPerOctave );
m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] );
case MidiProgramChange:
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 @@ -262,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
Loading

0 comments on commit 558ef8b

Please sign in to comment.