mirror of
https://github.com/LMMS/lmms.git
synced 2026-01-16 18:38:15 -05:00
* clang-tidy: Apply cppcoreguidelines-init-variables everywhere (treating NaNs as zeros) * Initialize msec and tick outside switch * Update plugins/Vestige/Vestige.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update plugins/Vestige/Vestige.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update plugins/Vestige/Vestige.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update plugins/VstEffect/VstEffectControls.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update plugins/VstEffect/VstEffectControls.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update plugins/VstEffect/VstEffectControls.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Use initialization with = * Use tabs * Use static_cast * Update DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Do not use tabs for alignment in src/core/DrumSynth.cpp Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com> * Move x variable inside loop * Use ternary operator for b variable * Revert "Use tabs" This reverts commit 07afd8a83f58b539c3673310b2aad4b63c9198a0. * Remove unnecessary variables in XpressiveView * Simplify initialization in Plugin * Combine declaration and initialization in EqCurve * Combine declaration and initialization in Song * Combine declaration and initialization in AudioAlsa * Combine declaration and initialization in EqCurve (again) * Missed some * Undo changes made to non-LMMS files * Undo indentation changes in SidInstrument.cpp * Combine declaration with assignment in IoHelper * Combine declaration with assignment using auto in Carla * Combine declaration with assignment * Combine declaration with assignment in BasicFilters * Simplify assignments in AudioFileProcessorWaveView::zoom * Simplify out sample variable in BitInvader * Remove sampleLength variable in DelayEffect * Move gain variable in DynamicsProcessor * Combine peak variable declaration with assignment in EqSpectrumView * Move left/right lfo variables in for loop in FlangerEffect * Use ternary operator for group variable in LadspaControlDialog * Combine declaration with assignment in Lb302 * Combine declaration with assignment in MidiExport * Combine declaration with assignment in MidiFile * Combine declaration with assignment in MidiImport * Use ternary operator for vel_adjusted variable in OpulenZ * Move tmpL and dcblkL variables in for loop in ReverbSC * Combine declaration with initialization in SlicerT * Combine declaration with assignment in SaSpectrumView * Combine declaration with assignment in SaWaterfallView * Combine declaration with assignment in StereoEnhancerEffect * Combine declaration with assignment in VibratingString * Combine declaration with assignment in VstEffectControls * Combine declaration with assignment in Xpressive * Combine declaration with assignment in AutomatableModel * Combine declaration with assignment in AutomationClip * Move sample variable in for loop in BandLimitedWave * Combine declaration with assignment in DataFile * Combine declaration with assignment in DrumSynth * Combine declaration with assignment in Effect * Remove redundant assignment to nphsLeft in InstrumentPlayHandle * Combine declaration with assignment in LadspaManager * Combine declaration with assignment in LinkedModelGroups * Combine declaration with assignment in MemoryHelper * Combine declaration with assignment in AudioAlsa * Combine declaration with assignment in AudioFileOgg * Combine declaration with assignment in AudioPortAudio * Combine declaration with assignment in AudioSoundIo * Combine declaration with assignment in Lv2Evbuf * Combine declaration with assignment in Lv2Proc * Combine declaration with assignment in main * Combine declaration with assignment in MidiAlsaRaw * Combine declaration with assignment in MidiAlsaSeq * Combine declaration with assignment in MidiController * Combine declaration with assignment in MidiJack * Combine declaration with assignment in MidiSndio * Combine declaration with assignment in ControlLayout * Combine declaration with assignment in MainWindow * Combine declaration with assignment in ProjectNotes * Use ternary operator for nextValue variable in AutomationClipView * Combine declaration with assignment in AutomationEditor * Move length variable in for-loop in PianoRoll * Combine declaration with assignment in ControllerConnectionDialog * Combine declaration with assignment in Graph * Combine declaration with assignment in LcdFloatSpinBox * Combine declaration with assignment in TimeDisplayWidget * Remove currentNote variable in InstrumentTrack * Combine declaration with assignment in DrumSynth (again) * Use ternary operator for factor variable in BitInvader * Use ternary operator for highestBandwich variable in EqCurve Bandwich? * Move sum variable into for loop in Graph * Fix format in MidiSndio * Fixup a few more * Cleanup error variables * Use ternary operators and combine declaration with initialization * Combine declaration with initialization * Update plugins/LadspaEffect/LadspaControlDialog.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update plugins/OpulenZ/OpulenZ.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update plugins/SpectrumAnalyzer/SaProcessor.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/midi/MidiAlsaRaw.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/gui/MainWindow.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/gui/clips/AutomationClipView.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/gui/editors/AutomationEditor.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/gui/widgets/Fader.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Move static_cast conversion into separate variable * Use real index when interpolating * Remove empty line * Make helpBtn a private member * Move controller type into separate variable * Fix format of DrumSynth::waveform function * Use tabs and static_cast * Remove redundant if branch * Refactor using static_cast/reinterpret_cast * Add std namespace prefix * Store repeated conditional into boolean variable * Cast to int before assigning to m_currentLength * Rename note_frames to noteFrames * Update src/core/Controller.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/core/DrumSynth.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Update src/gui/widgets/Graph.cpp Co-authored-by: Kevin Zander <veratil@gmail.com> * Revert changes that initialized variables redudantly For situations where the initialization is more complex or passed into a function by a pointer, we dont need to do initialization ourselves since it is already done for us, just in a different way. * Remove redundant err variable * Remove explicit check of err variable * Clean up changes and address review * Do not initialize to 0/nullptr when not needed * Wrap condition in parentheses for readability --------- Co-authored-by: Kevin Zander <veratil@gmail.com> Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
328 lines
7.8 KiB
C++
328 lines
7.8 KiB
C++
#ifndef MIDIFILE_HPP
|
|
#define MIDIFILE_HPP
|
|
|
|
/**
|
|
* Name: MidiFile.hpp
|
|
* Purpose: C++ re-write of the python module MidiFile.py
|
|
* Author: Mohamed Abdel Maksoud <mohamed at amaksoud.com>
|
|
*-----------------------------------------------------------------------------
|
|
* Name: MidiFile.py
|
|
* Purpose: MIDI file manipulation utilities
|
|
*
|
|
* Author: Mark Conway Wirt <emergentmusics) at (gmail . com>
|
|
*
|
|
* Created: 2008/04/17
|
|
* Copyright: (c) 2009 Mark Conway Wirt
|
|
* License: Please see License.txt for the terms under which this
|
|
* software is distributed.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
#include <array>
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
using std::set;
|
|
|
|
namespace MidiFile
|
|
{
|
|
|
|
const int TICKSPERBEAT = 128;
|
|
|
|
|
|
int writeVarLength(uint32_t val, uint8_t *buffer)
|
|
{
|
|
/*
|
|
Accept an input, and write a MIDI-compatible variable length stream
|
|
|
|
The MIDI format is a little strange, and makes use of so-called variable
|
|
length quantities. These quantities are a stream of bytes. If the most
|
|
significant bit is 1, then more bytes follow. If it is zero, then the
|
|
byte in question is the last in the stream
|
|
*/
|
|
int size = 0;
|
|
uint8_t little_endian[4];
|
|
uint8_t result = val & 0x7F;
|
|
little_endian[size++] = result;
|
|
val = val >> 7;
|
|
while (val > 0)
|
|
{
|
|
result = val & 0x7F;
|
|
result = result | 0x80;
|
|
little_endian[size++] = result;
|
|
val = val >> 7;
|
|
}
|
|
for (int i=0; i<size; i++)
|
|
{
|
|
buffer[i] = little_endian[size-i-1];
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
int writeBigEndian4(uint32_t val, uint8_t *buf)
|
|
{
|
|
buf[0] = val >> 24;
|
|
buf[1] = val >> 16 & 0xff;
|
|
buf[2] = val >> 8 & 0xff;
|
|
buf[3] = val & 0xff;
|
|
return 4;
|
|
}
|
|
|
|
int writeBigEndian2(uint16_t val, uint8_t *buf)
|
|
{
|
|
buf[0] = val >> 8 & 0xff;
|
|
buf[1] = val & 0xff;
|
|
return 2;
|
|
}
|
|
|
|
|
|
class MIDIHeader
|
|
{
|
|
// Class to encapsulate the MIDI header structure.
|
|
uint16_t numTracks;
|
|
uint16_t ticksPerBeat;
|
|
|
|
public:
|
|
|
|
MIDIHeader(uint16_t nTracks, uint16_t ticksPB=TICKSPERBEAT): numTracks(nTracks), ticksPerBeat(ticksPB) {}
|
|
|
|
inline int writeToBuffer(uint8_t *buffer, int start=0) const
|
|
{
|
|
// chunk ID
|
|
buffer[start++] = 'M'; buffer[start++] = 'T'; buffer[start++] = 'h'; buffer[start++] = 'd';
|
|
// chunk size (6 bytes always)
|
|
buffer[start++] = 0; buffer[start++] = 0; buffer[start++] = 0; buffer[start++] = 0x06;
|
|
// format: 1 (multitrack)
|
|
buffer[start++] = 0; buffer[start++] = 0x01;
|
|
|
|
start += writeBigEndian2(numTracks, buffer+start);
|
|
|
|
start += writeBigEndian2(ticksPerBeat, buffer+start);
|
|
|
|
return start;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
struct Event
|
|
{
|
|
uint32_t time;
|
|
uint32_t tempo;
|
|
string trackName;
|
|
enum {NOTE_ON, NOTE_OFF, TEMPO, PROG_CHANGE, TRACK_NAME} type;
|
|
// TODO make a union to save up space
|
|
uint8_t pitch;
|
|
uint8_t programNumber;
|
|
uint8_t duration;
|
|
uint8_t volume;
|
|
uint8_t channel;
|
|
|
|
Event() {time=tempo=pitch=programNumber=duration=volume=channel=0; trackName="";}
|
|
|
|
inline int writeToBuffer(uint8_t *buffer) const
|
|
{
|
|
int size = 0;
|
|
switch (type)
|
|
{
|
|
case NOTE_ON:
|
|
{
|
|
uint8_t code = 0x9 << 4 | channel;
|
|
size += writeVarLength(time, buffer+size);
|
|
buffer[size++] = code;
|
|
buffer[size++] = pitch;
|
|
buffer[size++] = volume;
|
|
break;
|
|
}
|
|
case NOTE_OFF:
|
|
{
|
|
uint8_t code = 0x8 << 4 | channel;
|
|
size += writeVarLength(time, buffer+size);
|
|
buffer[size++] = code;
|
|
buffer[size++] = pitch;
|
|
buffer[size++] = volume;
|
|
break;
|
|
}
|
|
case TEMPO:
|
|
{
|
|
uint8_t code = 0xFF;
|
|
size += writeVarLength(time, buffer+size);
|
|
buffer[size++] = code;
|
|
buffer[size++] = 0x51;
|
|
buffer[size++] = 0x03;
|
|
|
|
std::array<uint8_t, 4> fourbytes;
|
|
writeBigEndian4(int(60000000.0 / tempo), fourbytes.data());
|
|
|
|
//printf("tempo of %x translates to ", tempo);
|
|
/*
|
|
for (int i=0; i<3; i++) printf("%02x ", fourbytes[i+1]);
|
|
printf("\n");
|
|
*/
|
|
buffer[size++] = fourbytes[1];
|
|
buffer[size++] = fourbytes[2];
|
|
buffer[size++] = fourbytes[3];
|
|
break;
|
|
}
|
|
case PROG_CHANGE:
|
|
{
|
|
uint8_t code = 0xC << 4 | channel;
|
|
size += writeVarLength(time, buffer+size);
|
|
buffer[size++] = code;
|
|
buffer[size++] = programNumber;
|
|
break;
|
|
}
|
|
case TRACK_NAME:
|
|
{
|
|
size += writeVarLength(time, buffer+size);
|
|
buffer[size++] = 0xFF;
|
|
buffer[size++] = 0x03;
|
|
size += writeVarLength(trackName.size(), buffer+size);
|
|
trackName.copy((char *)(&buffer[size]), trackName.size());
|
|
size += trackName.size();
|
|
break;
|
|
// buffer[size++] = '\0';
|
|
// buffer[size++] = '\0';
|
|
}
|
|
}
|
|
return size;
|
|
} // writeEventsToBuffer
|
|
|
|
|
|
// events are sorted by their time
|
|
inline bool operator < (const Event& b) const {
|
|
return this->time < b.time ||
|
|
(this->time == b.time && this->type > b.type);
|
|
}
|
|
};
|
|
|
|
template<const int MAX_TRACK_SIZE>
|
|
class MIDITrack
|
|
{
|
|
// A class that encapsulates a MIDI track
|
|
// Nested class definitions.
|
|
vector<Event> events;
|
|
|
|
public:
|
|
uint8_t channel;
|
|
|
|
MIDITrack(): channel(0) {}
|
|
|
|
inline void addEvent(const Event &e)
|
|
{
|
|
Event E = e;
|
|
events.push_back(E);
|
|
}
|
|
|
|
inline void addNote(uint8_t pitch, uint8_t volume, double time, double duration)
|
|
{
|
|
Event event; event.channel = channel;
|
|
event.volume = volume;
|
|
|
|
event.type = Event::NOTE_ON; event.pitch = pitch; event.time= (uint32_t) (time * TICKSPERBEAT);
|
|
addEvent(event);
|
|
|
|
event.type = Event::NOTE_OFF; event.pitch = pitch; event.time=(uint32_t) ((time+duration) * TICKSPERBEAT);
|
|
addEvent(event);
|
|
|
|
//printf("note: %d-%d\n", (uint32_t) time * TICKSPERBEAT, (uint32_t)((time+duration) * TICKSPERBEAT));
|
|
}
|
|
|
|
inline void addName(const string &name, uint32_t time)
|
|
{
|
|
Event event; event.channel = channel;
|
|
event.type = Event::TRACK_NAME; event.time=time; event.trackName = name;
|
|
addEvent(event);
|
|
}
|
|
|
|
inline void addProgramChange(uint8_t prog, uint32_t time)
|
|
{
|
|
Event event; event.channel = channel;
|
|
event.type = Event::PROG_CHANGE; event.time=time; event.programNumber = prog;
|
|
addEvent(event);
|
|
}
|
|
|
|
inline void addTempo(uint32_t tempo, uint32_t time)
|
|
{
|
|
Event event;
|
|
event.channel = channel;
|
|
|
|
event.type = Event::TEMPO;
|
|
event.time = time;
|
|
event.tempo = tempo;
|
|
|
|
addEvent(event);
|
|
}
|
|
|
|
inline int writeMIDIToBuffer(uint8_t *buffer, int start=0) const
|
|
{
|
|
// Write the meta data and note data to the packed MIDI stream.
|
|
// Process the events in the eventList
|
|
|
|
start += writeEventsToBuffer(buffer, start);
|
|
|
|
// Write MIDI close event.
|
|
buffer[start++] = 0x00;
|
|
buffer[start++] = 0xFF;
|
|
buffer[start++] = 0x2F;
|
|
buffer[start++] = 0x00;
|
|
|
|
// return the entire length of the data and write to the header
|
|
|
|
return start;
|
|
}
|
|
|
|
inline int writeEventsToBuffer(uint8_t *buffer, int start=0) const
|
|
{
|
|
// Write the events in MIDIEvents to the MIDI stream.
|
|
vector<Event> _events = events;
|
|
std::sort(_events.begin(), _events.end());
|
|
vector<Event>::const_iterator it;
|
|
uint32_t time_last = 0;
|
|
for (it = _events.begin(); it!=_events.end(); ++it)
|
|
{
|
|
Event e = *it;
|
|
if (e.time < time_last){
|
|
printf("error: e.time=%d time_last=%d\n", e.time, time_last);
|
|
assert(false);
|
|
}
|
|
uint32_t tmp = e.time;
|
|
e.time -= time_last;
|
|
time_last = tmp;
|
|
start += e.writeToBuffer(buffer+start);
|
|
if (start >= MAX_TRACK_SIZE) {
|
|
break;
|
|
}
|
|
}
|
|
return start;
|
|
}
|
|
|
|
inline int writeToBuffer(uint8_t *buffer, int start=0) const
|
|
{
|
|
uint8_t eventsBuffer[MAX_TRACK_SIZE];
|
|
uint32_t events_size = writeMIDIToBuffer(eventsBuffer);
|
|
//printf(">> track %lu events took 0x%x bytes\n", events.size(), events_size);
|
|
|
|
// chunk ID
|
|
buffer[start++] = 'M'; buffer[start++] = 'T'; buffer[start++] = 'r'; buffer[start++] = 'k';
|
|
// chunk size
|
|
start += writeBigEndian4(events_size, buffer+start);
|
|
// copy events data
|
|
memmove(buffer+start, eventsBuffer, events_size);
|
|
start += events_size;
|
|
return start;
|
|
}
|
|
};
|
|
|
|
}; // namespace
|
|
|
|
#endif
|