From 04a4eaf06478f4d754c52073887dd03a4dd8ff86 Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 11 Aug 2020 11:50:50 +0200 Subject: [PATCH 1/8] Automatic formatting changes --- include/AutomationPattern.h | 2 +- include/DataFile.h | 1 - src/core/AutomationPattern.cpp | 5 ----- src/core/DataFile.cpp | 18 +++++++++--------- src/tracks/BBTrack.cpp | 26 +++++++++++++------------- src/tracks/InstrumentTrack.cpp | 2 +- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index cad9d0a1d00..aad991bb362 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -184,7 +184,7 @@ public slots: ProgressionTypes m_progressionType; bool m_dragging; - + bool m_isRecording; float m_lastRecordedValue; diff --git a/include/DataFile.h b/include/DataFile.h index 5890693da0a..07f8773e8ec 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -135,4 +135,3 @@ const QString LDF_VERSION_STRING = QString::number( LDF_MAJOR_VERSION ) + "." + #endif - diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 2fd1cea125f..e459d0ceb65 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -898,8 +898,3 @@ void AutomationPattern::generateTangents( timeMap::const_iterator it, it++; } } - - - - - diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 44cb920d844..755d33034a5 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -772,10 +772,10 @@ void DataFile::upgrade_0_4_0_rc2() void DataFile::upgrade_1_0_99() { jo_id_t last_assigned_id = 0; - + QList idList; findIds(documentElement(), idList); - + QDomNodeList list = elementsByTagName("ladspacontrols"); for(int i = 0; !list.item(i).isNull(); ++i) { @@ -791,22 +791,22 @@ void DataFile::upgrade_1_0_99() QDomElement me = createElement("data"); me.setAttribute("value", el.attribute("data")); me.setAttribute("scale_type", "log"); - + jo_id_t id; for(id = last_assigned_id + 1; idList.contains(id); id++) { } - + last_assigned_id = id; idList.append(id); me.setAttribute("id", id); el.appendChild(me); - + } } } - } + } } @@ -1047,7 +1047,7 @@ void DataFile::upgrade_1_3_0() QDomElement attribute = attributes.item( k ).toElement(); if( attribute.attribute( "name" ) == "file" && - ( attribute.attribute( "value" ) == "calf" || + ( attribute.attribute( "value" ) == "calf" || attribute.attribute( "value" ) == "calf.so" ) ) { attribute.setAttribute( "value", "veal" ); @@ -1311,7 +1311,7 @@ void DataFile::upgrade_1_3_0() }; iterate_ladspa_ports(effect, fn); } - + if( attribute.attribute( "name" ) == "plugin" && attribute.attribute( "value" ) == "StereoTools" ) { @@ -1536,7 +1536,7 @@ void findIds(const QDomElement& elem, QList& idList) idList.append(elem.attribute("id").toInt()); } QDomElement child = elem.firstChildElement(); - while(!child.isNull()) + while(!child.isNull()) { findIds(child, idList); child = child.nextSiblingElement(); diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index a779e2ea49b..ff8a09bc280 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -75,7 +75,7 @@ void BBTCO::saveSettings( QDomDocument & doc, QDomElement & element ) element.setAttribute( "len", length() ); element.setAttribute( "muted", isMuted() ); element.setAttribute( "color", color() ); - + if( m_useStyleColor ) { element.setAttribute( "usestyle", 1 ); @@ -106,10 +106,10 @@ void BBTCO::loadSettings( const QDomElement & element ) { setColor( QColor( element.attribute( "color" ).toUInt() ) ); } - + if( element.hasAttribute( "usestyle" ) ) { - if( element.attribute( "usestyle" ).toUInt() == 1 ) + if( element.attribute( "usestyle" ).toUInt() == 1 ) { m_useStyleColor = true; } @@ -212,12 +212,12 @@ void BBTCOView::paintEvent( QPaintEvent * ) QLinearGradient lingrad( 0, 0, 0, height() ); QColor c; bool muted = m_bbTCO->getTrack()->isMuted() || m_bbTCO->isMuted(); - + // state: selected, muted, default, user selected - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() - : ( m_bbTCO->m_useStyleColor ? painter.background().color() + c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() + : ( m_bbTCO->m_useStyleColor ? painter.background().color() : m_bbTCO->colorObj() ) ); - + lingrad.setColorAt( 0, c.lighter( 130 ) ); lingrad.setColorAt( 1, c.lighter( 70 ) ); @@ -232,7 +232,7 @@ void BBTCOView::paintEvent( QPaintEvent * ) { p.fillRect( rect(), c ); } - + // bar lines const int lineSize = 3; p.setPen( c.darker( 200 ) ); @@ -256,12 +256,12 @@ void BBTCOView::paintEvent( QPaintEvent * ) // inner border p.setPen( c.lighter( 130 ) ); p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, - rect().bottom() - TCO_BORDER_WIDTH ); + rect().bottom() - TCO_BORDER_WIDTH ); // outer border p.setPen( c.darker( 300 ) ); p.drawRect( 0, 0, rect().right(), rect().bottom() ); - + // draw the 'muted' pixmap only if the pattern was manualy muted if( m_bbTCO->isMuted() ) { @@ -270,11 +270,11 @@ void BBTCOView::paintEvent( QPaintEvent * ) p.drawPixmap( spacing, height() - ( size + spacing ), embed::getIconPixmap( "muted", size, size ) ); } - + p.end(); - + painter.drawPixmap( 0, 0, m_paintPixmap ); - + } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 222c604dfb9..a09408ceeb4 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -156,7 +156,7 @@ InstrumentTrack::~InstrumentTrack() autoAssignMidiDevice(false); s_autoAssignedTrack = NULL; } - + // kill all running notes and the iph silenceAllNotes( true ); From d956154f40f1ab9ec202e546e397188fd40bd1db Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 11 Aug 2020 11:59:07 +0200 Subject: [PATCH 2/8] Give clips an empty name by default, display all names - Stop giving clips the same name as their parent track on creation - Stop hiding clip names that match the parent track name - Never rename clips on track rename - Never clear clip name when a clip is copied to another track - Create an upgrade routine to clear default names from old projects (< 1.3.0-alpha-1) - Bump version to 1.3.0-alpha-1 --- CMakeLists.txt | 8 ++++---- include/DataFile.h | 1 + src/core/DataFile.cpp | 30 ++++++++++++++++++++++++++++++ src/core/Track.cpp | 7 ------- src/tracks/BBTrack.cpp | 5 +---- src/tracks/InstrumentTrack.cpp | 11 ----------- src/tracks/Pattern.cpp | 10 +++------- 7 files changed, 39 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ead95c32db..67d47bf55e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,10 +35,10 @@ SET(PROJECT_EMAIL "lmms-devel@lists.sourceforge.net") SET(PROJECT_DESCRIPTION "${PROJECT_NAME_UCASE} - Free music production software") SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}") SET(VERSION_MAJOR "1") -SET(VERSION_MINOR "2") -SET(VERSION_RELEASE "2") -SET(VERSION_STAGE "") -SET(VERSION_BUILD "0") +SET(VERSION_MINOR "3") +SET(VERSION_RELEASE "0") +SET(VERSION_STAGE "alpha") +SET(VERSION_BUILD "1") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}") IF(VERSION_STAGE) SET(VERSION "${VERSION}-${VERSION_STAGE}") diff --git a/include/DataFile.h b/include/DataFile.h index 07f8773e8ec..dcf05c932ae 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -108,6 +108,7 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_1_1_91(); void upgrade_1_2_0_rc3(); void upgrade_1_2_0_rc2_42(); + void upgrade_1_3_0_alpha_1(); void upgrade_1_3_0(); void upgrade(); diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 755d33034a5..a4efdcccdc2 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1335,6 +1335,35 @@ void DataFile::upgrade_1_3_0() } } +void DataFile::upgrade_1_3_0_alpha_1() +{ + QDomNodeList tracks = elementsByTagName("track"); + + auto clearDefaultNames = [](QDomNodeList clips, QString trackName) + { + for (int j = 0; !clips.item(j).isNull(); ++j) + { + QDomElement clip = clips.item(j).toElement(); + QString clipName = clip.attribute("name", ""); + if (clipName == trackName){ clip.setAttribute("name", ""); } + } + }; + + for (int i = 0; !tracks.item(i).isNull(); ++i) + { + QDomElement track = tracks.item(i).toElement(); + QString trackName = track.attribute("name", ""); + + QDomNodeList instClips = elementsByTagName("pattern"); + QDomNodeList autoClips = elementsByTagName("automationpattern"); + QDomNodeList bbClips = elementsByTagName("bbtco"); + + clearDefaultNames(instClips, trackName); + clearDefaultNames(autoClips, trackName); + clearDefaultNames(bbClips, trackName); + } +} + void DataFile::upgrade() { @@ -1417,6 +1446,7 @@ void DataFile::upgrade() upgrade_1_2_0_rc3(); upgrade_1_2_0_rc2_42(); } + if (version < "1.3.0-alpha-1"){ upgrade_1_3_0_alpha_1(); } if( version < "1.3.0" ) { upgrade_1_3_0(); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index f79a34a15c9..fe4941ef425 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1865,13 +1865,6 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, { tco->selectViewOnCreate( true ); } - - //check tco name, if the same as source track name dont copy - QString sourceTrackName = outerTCOElement.attributeNode( "trackName" ).value(); - if( tco->name() == sourceTrackName ) - { - tco->setName( "" ); - } } AutomationPattern::resolveAllIDs(); diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index ff8a09bc280..498a904afeb 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -290,10 +290,7 @@ void BBTCOView::openInBBEditor() -void BBTCOView::resetName() -{ - m_bbTCO->setName( m_bbTCO->getTrack()->name() ); -} +void BBTCOView::resetName() { m_bbTCO->setName(""); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a09408ceeb4..b40b6e65785 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -535,17 +535,6 @@ void InstrumentTrack::deleteNotePluginData( NotePlayHandle* n ) void InstrumentTrack::setName( const QString & _new_name ) { - // when changing name of track, also change name of those patterns, - // which have the same name as the instrument-track - for( int i = 0; i < numOfTCOs(); ++i ) - { - Pattern* p = dynamic_cast( getTCO( i ) ); - if( ( p != NULL && p->name() == name() ) || p->name() == "" ) - { - p->setName( _new_name ); - } - } - Track::setName( _new_name ); m_midiPort.setName( name() ); m_audioPort.setName( name() ); diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 40f11b3cb30..bcb18bd5990 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -58,7 +58,6 @@ Pattern::Pattern( InstrumentTrack * _instrument_track ) : m_patternType( BeatPattern ), m_steps( MidiTime::stepsPerBar() ) { - setName( _instrument_track->name() ); if( _instrument_track->trackContainer() == Engine::getBBTrackContainer() ) { @@ -649,10 +648,7 @@ void PatternView::setGhostInPianoRoll() -void PatternView::resetName() -{ - m_pat->setName( m_pat->m_instrumentTrack->name() ); -} +void PatternView::resetName() { m_pat->setName(""); } @@ -887,8 +883,8 @@ void PatternView::paintEvent( QPaintEvent * ) // Check whether we will paint a text box and compute its potential height // This is needed so we can paint the notes underneath it. - bool const isDefaultName = m_pat->name() == m_pat->instrumentTrack()->name(); - bool const drawTextBox = !beatPattern && !isDefaultName; + bool const drawName = !m_pat->name().isEmpty(); + bool const drawTextBox = !beatPattern && drawName; // TODO Warning! This might cause problems if TrackContentObjectView::paintTextLabel changes int textBoxHeight = 0; From 27460c8870fbf162185e26b5251aa4a40d5dcdff Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 18 Sep 2020 16:48:47 +0200 Subject: [PATCH 3/8] Revert now-unnecessary version bump --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67d47bf55e3..4ead95c32db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,10 +35,10 @@ SET(PROJECT_EMAIL "lmms-devel@lists.sourceforge.net") SET(PROJECT_DESCRIPTION "${PROJECT_NAME_UCASE} - Free music production software") SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}") SET(VERSION_MAJOR "1") -SET(VERSION_MINOR "3") -SET(VERSION_RELEASE "0") -SET(VERSION_STAGE "alpha") -SET(VERSION_BUILD "1") +SET(VERSION_MINOR "2") +SET(VERSION_RELEASE "2") +SET(VERSION_STAGE "") +SET(VERSION_BUILD "0") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}") IF(VERSION_STAGE) SET(VERSION "${VERSION}-${VERSION_STAGE}") From 9680a803b19163c0488abf7ddc7403984ae89926 Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 11 Aug 2020 14:57:27 +0200 Subject: [PATCH 4/8] Merge with master and fix conflicts --- .circleci/config.yml | 6 +- CMakeLists.txt | 2 +- README.md | 5 +- cmake/CMakeLists.txt | 2 +- cmake/modules/VersionInfo.cmake | 36 +- data/themes/classic/style.css | 1 + data/themes/default/style.css | 1 + include/AudioAlsa.h | 2 +- include/AudioDummy.h | 2 +- include/AudioJack.h | 2 +- include/AudioOss.h | 2 +- include/AudioPortAudio.h | 2 +- include/AudioPulseAudio.h | 2 +- include/AudioSdl.h | 2 +- include/AudioSndio.h | 2 +- include/AudioSoundIo.h | 2 +- include/AutomationEditor.h | 1 + include/ComboBox.h | 4 +- include/ConfigManager.h | 12 +- include/DataFile.h | 20 +- include/DeprecationHelper.h | 63 + include/FadeButton.h | 8 +- include/Fader.h | 11 +- include/FileDialog.h | 3 +- include/InstrumentSoundShaping.h | 2 +- include/LinkedModelGroupViews.h | 2 +- include/LinkedModelGroups.h | 2 +- include/Lv2ControlBase.h | 2 +- include/MainWindow.h | 2 +- include/Oscillator.h | 10 +- include/ProjectVersion.h | 24 +- include/StepRecorder.h | 6 +- include/SubWindow.h | 2 +- include/lmms_math.h | 23 +- plugins/Amplifier/Amplifier.cpp | 2 +- plugins/BassBooster/BassBooster.cpp | 2 +- plugins/Bitcrush/Bitcrush.cpp | 2 +- plugins/CrossoverEQ/CrossoverEQ.cpp | 2 +- plugins/Delay/DelayEffect.cpp | 2 +- plugins/DualFilter/DualFilter.cpp | 2 +- .../DualFilter/DualFilterControlDialog.cpp | 4 +- plugins/Eq/EqEffect.cpp | 2 +- plugins/Flanger/FlangerEffect.cpp | 2 +- plugins/FreeBoy/FreeBoy.cpp | 2 +- plugins/GigPlayer/GigPlayer.cpp | 2 +- plugins/GigPlayer/PatchesDialog.h | 2 +- plugins/HydrogenImport/HydrogenImport.cpp | 2 +- plugins/LadspaEffect/LadspaEffect.cpp | 2 +- plugins/Lv2Effect/Lv2Effect.cpp | 2 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 2 +- plugins/MidiExport/MidiExport.cpp | 2 +- plugins/MidiImport/MidiImport.cpp | 51 +- plugins/MidiImport/portsmf/algrd_internal.h | 9 +- plugins/MidiImport/portsmf/allegro.cpp | 1260 ++++++++++---- plugins/MidiImport/portsmf/allegro.h | 362 +++- plugins/MidiImport/portsmf/allegrord.cpp | 1533 +++++++++-------- plugins/MidiImport/portsmf/allegrosmfrd.cpp | 906 +++++----- plugins/MidiImport/portsmf/allegrosmfwr.cpp | 60 +- plugins/MidiImport/portsmf/allegrowr.cpp | 28 +- plugins/MidiImport/portsmf/mfmidi.cpp | 34 +- plugins/MidiImport/portsmf/mfmidi.h | 19 +- plugins/MidiImport/portsmf/strparse.cpp | 12 +- plugins/MidiImport/portsmf/strparse.h | 2 +- plugins/MidiImport/portsmf/trace.cpp | 2 +- plugins/MultitapEcho/MultitapEcho.cpp | 2 +- plugins/OpulenZ/OpulenZ.cpp | 2 +- plugins/ReverbSC/ReverbSC.cpp | 2 +- plugins/SpectrumAnalyzer/Analyzer.cpp | 2 +- plugins/SpectrumAnalyzer/SaControlsDialog.cpp | 12 +- plugins/Vectorscope/Vectorscope.cpp | 2 +- plugins/VstEffect/VstEffect.cpp | 2 +- plugins/VstEffect/VstEffectControls.cpp | 2 +- plugins/Xpressive/Xpressive.cpp | 2 +- .../audio_file_processor.cpp | 6 +- plugins/bit_invader/bit_invader.cpp | 2 +- plugins/carlapatchbay/carlapatchbay.cpp | 2 +- plugins/carlarack/carlarack.cpp | 2 +- .../dynamics_processor/dynamics_processor.cpp | 2 +- plugins/kicker/kicker.cpp | 2 +- plugins/ladspa_browser/ladspa_browser.cpp | 2 +- plugins/ladspa_browser/ladspa_port_dialog.cpp | 2 +- plugins/lb302/lb302.cpp | 11 +- plugins/monstro/Monstro.cpp | 12 +- plugins/nes/Nes.cpp | 2 +- plugins/organic/organic.cpp | 2 +- plugins/patman/patman.cpp | 2 +- .../peak_controller_effect.cpp | 2 +- plugins/sf2_player/patches_dialog.h | 2 +- plugins/sf2_player/sf2_player.cpp | 2 +- plugins/sfxr/sfxr.cpp | 2 +- plugins/sid/sid_instrument.cpp | 2 +- plugins/stereo_enhancer/stereo_enhancer.cpp | 2 +- plugins/stereo_matrix/stereo_matrix.cpp | 2 +- plugins/stk/mallets/mallets.cpp | 4 +- .../triple_oscillator/TripleOscillator.cpp | 2 +- plugins/vestige/vestige.cpp | 4 +- plugins/vibed/vibed.cpp | 2 +- plugins/vst_base/VstPlugin.cpp | 2 +- plugins/watsyn/Watsyn.cpp | 2 +- plugins/waveshaper/waveshaper.cpp | 2 +- plugins/zynaddsubfx/ZynAddSubFx.cpp | 2 +- src/core/AutomationPattern.cpp | 5 + src/core/ConfigManager.cpp | 67 +- src/core/DataFile.cpp | 219 +-- src/core/InstrumentSoundShaping.cpp | 18 +- src/core/LadspaManager.cpp | 2 + src/core/Oscillator.cpp | 3 +- src/core/Plugin.cpp | 2 +- src/core/PluginFactory.cpp | 5 + src/core/ProjectVersion.cpp | 175 +- src/core/Song.cpp | 4 + src/core/Track.cpp | 24 +- src/core/audio/AudioFileOgg.cpp | 11 +- src/core/audio/AudioPortAudio.cpp | 4 +- src/core/lv2/Lv2Proc.cpp | 2 + src/core/main.cpp | 3 +- src/gui/AudioDeviceSetupWidget.cpp | 2 +- src/gui/ControllerConnectionDialog.cpp | 2 +- src/gui/EffectSelectDialog.cpp | 2 +- src/gui/MidiSetupWidget.cpp | 2 +- src/gui/PluginBrowser.cpp | 4 +- src/gui/SetupDialog.cpp | 66 +- src/gui/dialogs/FileDialog.cpp | 4 +- src/gui/dialogs/VersionedSaveDialog.cpp | 5 +- src/gui/editors/AutomationEditor.cpp | 88 +- src/gui/editors/BBEditor.cpp | 2 +- src/gui/editors/PianoRoll.cpp | 61 +- src/gui/editors/SongEditor.cpp | 34 +- src/gui/widgets/ComboBox.cpp | 6 +- src/gui/widgets/Controls.cpp | 2 +- src/gui/widgets/FadeButton.cpp | 8 +- src/gui/widgets/Fader.cpp | 12 +- .../widgets/InstrumentSoundShapingView.cpp | 4 +- src/gui/widgets/Knob.cpp | 19 +- src/gui/widgets/LcdSpinBox.cpp | 7 +- src/gui/widgets/LcdWidget.cpp | 19 +- src/gui/widgets/LedCheckbox.cpp | 5 +- src/gui/widgets/TabWidget.cpp | 13 +- src/tracks/InstrumentTrack.cpp | 5 - src/tracks/Pattern.cpp | 32 +- src/tracks/SampleTrack.cpp | 24 +- tests/src/core/ProjectVersionTest.cpp | 30 + 142 files changed, 3403 insertions(+), 2292 deletions(-) create mode 100644 include/DeprecationHelper.h diff --git a/.circleci/config.yml b/.circleci/config.yml index f7f509d7e90..fe5b1f2fd7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -170,7 +170,7 @@ jobs: environment: <<: *common_environment macos: - xcode: "9.3.1" + xcode: "9.4.1" steps: - checkout - *init @@ -179,8 +179,8 @@ jobs: - run: name: Install Homebrew dependencies command: | - # unlink Homebrew's python 2 to prevent an node-gyp error - brew unlink python@2 || true + # uninstall Homebrew's python 2 to prevent errors on brew install + brew uninstall python@2 || true brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla - run: name: Install nodejs dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ead95c32db..aaeec055f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ IF(VERSION_STAGE) SET(VERSION "${VERSION}-${VERSION_STAGE}") ENDIF() IF(VERSION_BUILD) - SET(VERSION "${VERSION}.${VERSION_BUILD}") + SET(VERSION "${VERSION}-${VERSION_BUILD}") ENDIF() # Override version information for non-base builds diff --git a/README.md b/README.md index 1061ecff600..b1bea70a0c6 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,6 @@ Information about what you can do and how can be found in the Before coding a new big feature, please _always_ [file an issue](https://github.com/LMMS/lmms/issues/new) for your idea and -suggestions about your feature and about the intended implementation on GitHub -or post to the LMMS developers mailinglist (lmms-devel@lists.sourceforge.net) -and wait for replies! Maybe there are different ideas, improvements, hints or +suggestions about your feature and about the intended implementation on GitHub, +or ask in one of the tech channels on Discord and wait for replies! Maybe there are different ideas, improvements, or hints, or maybe your feature is not welcome/needed at the moment. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index b27dec91e0f..833fad5819f 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -9,7 +9,7 @@ IF(VERSION_STAGE) SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${VERSION_STAGE}") ENDIF() IF(VERSION_BUILD) - SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}.${VERSION_BUILD}") + SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${VERSION_BUILD}") ENDIF() SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME_UCASE}") SET(CPACK_SOURCE_GENERATOR "TBZ2") diff --git a/cmake/modules/VersionInfo.cmake b/cmake/modules/VersionInfo.cmake index cf6932cbbba..9571514a6eb 100644 --- a/cmake/modules/VersionInfo.cmake +++ b/cmake/modules/VersionInfo.cmake @@ -1,29 +1,56 @@ FIND_PACKAGE(Git) IF(GIT_FOUND AND NOT FORCE_VERSION) - # Look for git tag information (e.g. Tagged: "v1.0.0", Non-tagged: "v1.0.0-123-a1b2c3d") + SET(MAJOR_VERSION 0) + SET(MINOR_VERSION 0) + SET(PATCH_VERSION 0) + # Look for git tag information (e.g. Tagged: "v1.0.0", Untagged: "v1.0.0-123-a1b2c3d") + # Untagged format: [latest tag]-[number of commits]-[latest commit hash] EXECUTE_PROCESS( COMMAND "${GIT_EXECUTABLE}" describe --tags --match v[0-9].[0-9].[0-9]* OUTPUT_VARIABLE GIT_TAG WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 10 OUTPUT_STRIP_TRAILING_WHITESPACE) + # Read: TAG_LIST = GIT_TAG.split("-") STRING(REPLACE "-" ";" TAG_LIST "${GIT_TAG}") + # Read: TAG_LIST_LENGTH = TAG_LIST.length() LIST(LENGTH TAG_LIST TAG_LIST_LENGTH) + # Untagged versions contain at least 2 dashes, giving 3 strings on split. + # Hence, for untagged versions TAG_LIST_LENGTH = [dashes in latest tag] + 3. + # Corollary: if TAG_LIST_LENGTH <= 2, the version must be tagged. IF(TAG_LIST_LENGTH GREATER 0) + # Set FORCE_VERSION to TAG_LIST[0], strip any 'v's to get MAJ.MIN.PAT LIST(GET TAG_LIST 0 FORCE_VERSION) STRING(REPLACE "v" "" FORCE_VERSION "${FORCE_VERSION}") + # Split FORCE_VERSION on '.' and populate MAJOR/MINOR/PATCH_VERSION + STRING(REPLACE "." ";" MAJ_MIN_PAT "${FORCE_VERSION}") + LIST(GET MAJ_MIN_PAT 0 MAJOR_VERSION) + LIST(GET MAJ_MIN_PAT 1 MINOR_VERSION) + LIST(GET MAJ_MIN_PAT 2 PATCH_VERSION) ENDIF() + # 1 dash total: Dash in latest tag, no additional commits => pre-release IF(TAG_LIST_LENGTH EQUAL 2) LIST(GET TAG_LIST 1 VERSION_STAGE) SET(FORCE_VERSION "${FORCE_VERSION}-${VERSION_STAGE}") + # 2 dashes: Assume untagged with no dashes in latest tag name => stable + commits ELSEIF(TAG_LIST_LENGTH EQUAL 3) + # Get the number of commits and latest commit hash LIST(GET TAG_LIST 1 EXTRA_COMMITS) - SET(FORCE_VERSION "${FORCE_VERSION}.${EXTRA_COMMITS}") + LIST(GET TAG_LIST 2 COMMIT_HASH) + # Bump the patch version + MATH(EXPR PATCH_VERSION "${PATCH_VERSION}+1") + # Set the version to MAJOR.MINOR.PATCH-EXTRA_COMMITS+COMMIT_HASH + SET(FORCE_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}") + SET(FORCE_VERSION "${FORCE_VERSION}-${EXTRA_COMMITS}+${COMMIT_HASH}") + # 3 dashes: Assume untagged with 1 dash in latest tag name => pre-release + commits ELSEIF(TAG_LIST_LENGTH EQUAL 4) + # Get pre-release stage, number of commits, and latest commit hash LIST(GET TAG_LIST 1 VERSION_STAGE) LIST(GET TAG_LIST 2 EXTRA_COMMITS) - SET(FORCE_VERSION - "${FORCE_VERSION}-${VERSION_STAGE}.${EXTRA_COMMITS}") + LIST(GET TAG_LIST 3 COMMIT_HASH) + # Set the version to MAJOR.MINOR.PATCH-VERSION_STAGE.EXTRA_COMMITS+COMMIT_HASH + SET(FORCE_VERSION "${FORCE_VERSION}-${VERSION_STAGE}") + SET(FORCE_VERSION "${FORCE_VERSION}.${EXTRA_COMMITS}+${COMMIT_HASH}") ENDIF() ENDIF() @@ -74,4 +101,3 @@ MESSAGE("\n" "* Override version: -DFORCE_VERSION=x.x.x-x\n" "* Ignore Git information: -DFORCE_VERSION=internal\n" ) - diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 3e64c9da5a9..65b496617f1 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -35,6 +35,7 @@ QLineEdit { border: 2px inset rgba(91,101,113,128); background: #49515b; color: #e0e0e0; + selection-background-color: #202020; } diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 4dee86788ec..832da176f28 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -62,6 +62,7 @@ QLineEdit { border: 1px; background: #101213; color: #d1d8e4; + selection-background-color: #17793b; } QLineEdit:read-only { diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index b1aa9647a61..4bfd0217e31 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -71,7 +71,7 @@ class AudioAlsa : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "ALSA (Advanced Linux Sound Architecture)" ); } diff --git a/include/AudioDummy.h b/include/AudioDummy.h index 0772c69eb36..cf77491bc3f 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -48,7 +48,7 @@ class AudioDummy : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "Dummy (no sound output)" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "Dummy (no sound output)" ); } diff --git a/include/AudioJack.h b/include/AudioJack.h index a80e8855201..844aa886d87 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -63,7 +63,7 @@ class AudioJack : public QObject, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "JACK (JACK Audio Connection Kit)" ); } diff --git a/include/AudioOss.h b/include/AudioOss.h index 9e4787ff202..c631bcedddc 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -48,7 +48,7 @@ class AudioOss : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "OSS (Open Sound System)" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "OSS (Open Sound System)" ); } static QString probeDevice(); diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index e1288c3a45d..5279492cee7 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -72,7 +72,7 @@ class AudioPortAudio : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "PortAudio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" ); } diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index e65180a74a3..b92a386b99f 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -50,7 +50,7 @@ class AudioPulseAudio : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "PulseAudio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PulseAudio" ); } static QString probeDevice(); diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 93f23abed22..1bda446b290 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -51,7 +51,7 @@ class AudioSdl : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "SDL (Simple DirectMedia Layer)" ); } diff --git a/include/AudioSndio.h b/include/AudioSndio.h index f8cf56848a6..0cc88facfa4 100644 --- a/include/AudioSndio.h +++ b/include/AudioSndio.h @@ -49,7 +49,7 @@ class AudioSndio : public QThread, public AudioDevice inline static QString name( void ) { - return QT_TRANSLATE_NOOP( "setupWidget", "sndio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "sndio" ); } class setupWidget : public AudioDeviceSetupWidget diff --git a/include/AudioSoundIo.h b/include/AudioSoundIo.h index f743ad67629..79b586f0645 100644 --- a/include/AudioSoundIo.h +++ b/include/AudioSoundIo.h @@ -61,7 +61,7 @@ class AudioSoundIo : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "soundio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "soundio" ); } class setupWidget : public AudioDeviceSetupWidget diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 0a288f8afcf..4813759697f 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -209,6 +209,7 @@ protected slots: float m_bottomLevel; float m_topLevel; + void centerTopBottomScroll(); void updateTopBottomLevels(); QScrollBar * m_leftRightScroll; diff --git a/include/ComboBox.h b/include/ComboBox.h index d530c9d92f7..1ab1c240d80 100644 --- a/include/ComboBox.h +++ b/include/ComboBox.h @@ -32,8 +32,6 @@ #include "ComboBoxModel.h" #include "AutomatableModelView.h" - - class LMMS_EXPORT ComboBox : public QWidget, public IntModelView { Q_OBJECT @@ -51,6 +49,8 @@ class LMMS_EXPORT ComboBox : public QWidget, public IntModelView return castModel(); } + static constexpr int DEFAULT_HEIGHT = 22; + public slots: void selectNext(); void selectPrevious(); diff --git a/include/ConfigManager.h b/include/ConfigManager.h index de22d22af11..e5df02ff5e9 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -39,7 +39,6 @@ class LmmsCore; - const QString PROJECTS_PATH = "projects/"; const QString TEMPLATE_PATH = "templates/"; const QString PRESETS_PATH = "presets/"; @@ -55,6 +54,9 @@ const QString PORTABLE_MODE_FILE = "/portable_mode.txt"; class LMMS_EXPORT ConfigManager : public QObject { Q_OBJECT + + using UpgradeMethod = void(ConfigManager::*)(); + public: static inline ConfigManager * inst() { @@ -219,6 +221,10 @@ class LMMS_EXPORT ConfigManager : public QObject return m_version; } + // Used when the configversion attribute is not present in a configuration file. + // Returns the appropriate config file version based on the LMMS version. + unsigned int legacyConfigVersion(); + QString defaultVersion() const; @@ -270,6 +276,9 @@ class LMMS_EXPORT ConfigManager : public QObject void upgrade_1_1_91(); void upgrade(); + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + QString m_workingDir; QString m_dataDir; QString m_vstDir; @@ -286,6 +295,7 @@ class LMMS_EXPORT ConfigManager : public QObject QString m_backgroundPicFile; QString m_lmmsRcFile; QString m_version; + unsigned int m_configVersion; QStringList m_recentlyOpenedProjects; typedef QVector > stringPairVector; diff --git a/include/DataFile.h b/include/DataFile.h index dcf05c932ae..5d6ead5adb3 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -31,12 +31,16 @@ #include "lmms_export.h" #include "MemoryManager.h" +#include "ProjectVersion.h" class QTextStream; class LMMS_EXPORT DataFile : public QDomDocument { MM_OPERATORS + + using UpgradeMethod = void(DataFile::*)(); + public: enum Types { @@ -84,6 +88,8 @@ class LMMS_EXPORT DataFile : public QDomDocument return m_type; } + unsigned int legacyFileVersion(); + private: static Type type( const QString& typeName ); static QString typeName( Type type ); @@ -107,9 +113,13 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_1_1_0(); void upgrade_1_1_91(); void upgrade_1_2_0_rc3(); - void upgrade_1_2_0_rc2_42(); - void upgrade_1_3_0_alpha_1(); void upgrade_1_3_0(); + void upgrade_noHiddenClipNames(); + + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + // List of ProjectVersions for the legacyFileVersion method + static const std::vector UPGRADE_VERSIONS; void upgrade(); @@ -126,13 +136,9 @@ class LMMS_EXPORT DataFile : public QDomDocument QDomElement m_content; QDomElement m_head; Type m_type; + unsigned int m_fileVersion; } ; -const int LDF_MAJOR_VERSION = 1; -const int LDF_MINOR_VERSION = 0; -const QString LDF_VERSION_STRING = QString::number( LDF_MAJOR_VERSION ) + "." + QString::number( LDF_MINOR_VERSION ); - - #endif diff --git a/include/DeprecationHelper.h b/include/DeprecationHelper.h new file mode 100644 index 00000000000..bef4ea9b954 --- /dev/null +++ b/include/DeprecationHelper.h @@ -0,0 +1,63 @@ +/* + * DeprecationHelper.h - This file contains the declarations of helper functions + * which helps centralize the #ifdefs preprocessors regarding deprecation based on Qt versions. + * The functions are defined differently based on the callers' Qt versions. + * + * Copyright (c) 2020 Tien Dat Nguyen + * + * This file is part of LMMS - https://lmms.io + * + * 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 DEPRECATIONHELPER_H +#define DEPRECATIONHELPER_H + +#include +#include + +/** + * @brief horizontalAdvance is a backwards-compatible adapter for + * QFontMetrics::horizontalAdvance and width functions. + * @param metrics + * @param text + * @return text's horizontal advance based on metrics. + */ +inline int horizontalAdvance(const QFontMetrics& metrics, const QString& text) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return metrics.horizontalAdvance(text); +#else + return metrics.width(text); +#endif +} + +/** + * @brief position is a backwards-compatible adapter for + * QWheelEvent::position and pos functions. + * @param wheelEvent + * @return the position of wheelEvent + */ +inline QPoint position(QWheelEvent *wheelEvent) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + return wheelEvent->position().toPoint(); +#else + return wheelEvent->pos(); +#endif +} +#endif // DEPRECATIONHELPER_H diff --git a/include/FadeButton.h b/include/FadeButton.h index 54703d19476..dfffe93a284 100644 --- a/include/FadeButton.h +++ b/include/FadeButton.h @@ -26,9 +26,9 @@ #ifndef FADE_BUTTON_H #define FADE_BUTTON_H -#include #include #include +#include class FadeButton : public QAbstractButton @@ -55,8 +55,8 @@ public slots: private: - QTime m_stateTimer; - QTime m_releaseTimer; + QElapsedTimer m_stateTimer; + QElapsedTimer m_releaseTimer; // the default color of the widget QColor m_normalColor; @@ -66,7 +66,7 @@ public slots: QColor m_holdColor; int activeNotes; - QColor fadeToColor(QColor, QColor, QTime, float); + QColor fadeToColor(QColor, QColor, QElapsedTimer, float); } ; diff --git a/include/Fader.h b/include/Fader.h index 2072154459d..bf1e7215e35 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -48,9 +48,10 @@ #ifndef FADER_H #define FADER_H -#include -#include +#include #include +#include + #include "AutomatableModelView.h" @@ -130,7 +131,7 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView return height() - ( ( height() - m_knob->height() ) * ( realVal / fRange ) ); } - void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTime &lastPeakTime ); + void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QElapsedTimer &lastPeakTimer ); int calculateDisplayPeak( float fPeak ); void updateTextFloat(); @@ -144,8 +145,8 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView float m_fMinPeak; float m_fMaxPeak; - QTime m_lastPeakTime_L; - QTime m_lastPeakTime_R; + QElapsedTimer m_lastPeakTimer_L; + QElapsedTimer m_lastPeakTimer_R; static QPixmap * s_back; static QPixmap * s_leds; diff --git a/include/FileDialog.h b/include/FileDialog.h index c3db2393d45..6e29703b8a1 100644 --- a/include/FileDialog.h +++ b/include/FileDialog.h @@ -46,8 +46,7 @@ class LMMS_EXPORT FileDialog : public QFileDialog const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString(), - QString *selectedFilter = 0, - QFileDialog::Options options = 0); + QString *selectedFilter = 0); void clearSelection(); }; diff --git a/include/InstrumentSoundShaping.h b/include/InstrumentSoundShaping.h index 1b8df38d3f1..b037f615a77 100644 --- a/include/InstrumentSoundShaping.h +++ b/include/InstrumentSoundShaping.h @@ -74,7 +74,7 @@ class InstrumentSoundShaping : public Model, public JournallingObject FloatModel m_filterCutModel; FloatModel m_filterResModel; - static const QString targetNames[InstrumentSoundShaping::NumTargets][3]; + static const char *const targetNames[InstrumentSoundShaping::NumTargets][3]; friend class InstrumentSoundShapingView; diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 390d2556890..46ba4c3ab55 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -98,7 +98,7 @@ class LinkedModelGroupsView void modelChanged(class LinkedModelGroups* ctrlBase); private: - //! The base class must return the adressed group view, + //! The base class must return the addressed group view, //! which has the same value as "this" virtual LinkedModelGroupView* getGroupView() = 0; }; diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 355290d9aee..686f09cba36 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -42,7 +42,7 @@ /** Base class for a group of linked models - See the LinkedModelGroup class for explenations + See the LinkedModelGroup class for explanations Features: * Models are stored by their QObject::objectName diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 9f1b54992cd..d6591a50d7b 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -52,7 +52,7 @@ class PluginIssue; This class would usually be a Model subclass. However, Qt doesn't allow this: - * inhertiting only from Model will cause diamond inheritance for QObject, + * inheriting only from Model will cause diamond inheritance for QObject, which will cause errors with Q_OBJECT * making this a direct subclass of Instrument resp. EffectControls would require CRTP, which would make this class a template class, which would diff --git a/include/MainWindow.h b/include/MainWindow.h index 15112456853..a179e651e49 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -61,7 +61,7 @@ class MainWindow : public QMainWindow void addSpacingToToolBar( int _size ); // wrap the widget with a window decoration and add it to the workspace - LMMS_EXPORT SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags=0); + LMMS_EXPORT SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags = QFlag(0)); /// diff --git a/include/Oscillator.h b/include/Oscillator.h index 408e69dbc6c..912bcdc0977 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -97,7 +97,7 @@ class LMMS_EXPORT Oscillator static inline sample_t triangleSample( const float _sample ) { - const float ph = fraction( _sample ); + const float ph = absFraction( _sample ); if( ph <= 0.25f ) { return ph * 4.0f; @@ -111,17 +111,17 @@ class LMMS_EXPORT Oscillator static inline sample_t sawSample( const float _sample ) { - return -1.0f + fraction( _sample ) * 2.0f; + return -1.0f + absFraction( _sample ) * 2.0f; } static inline sample_t squareSample( const float _sample ) { - return ( fraction( _sample ) > 0.5f ) ? -1.0f : 1.0f; + return ( absFraction( _sample ) > 0.5f ) ? -1.0f : 1.0f; } static inline sample_t moogSawSample( const float _sample ) { - const float ph = fraction( _sample ); + const float ph = absFraction( _sample ); if( ph < 0.5f ) { return -1.0f + ph * 4.0f; @@ -131,7 +131,7 @@ class LMMS_EXPORT Oscillator static inline sample_t expSample( const float _sample ) { - float ph = fraction( _sample ); + float ph = absFraction( _sample ); if( ph > 0.5f ) { ph = 1.0f - ph; diff --git a/include/ProjectVersion.h b/include/ProjectVersion.h index 5f7bf09b3c1..e938ccc81c7 100644 --- a/include/ProjectVersion.h +++ b/include/ProjectVersion.h @@ -28,6 +28,9 @@ #define PROJECT_VERSION_H #include +#include + +#include /*! \brief Version number parsing and comparison * @@ -36,16 +39,16 @@ class ProjectVersion { public: - enum CompareType { Major, Minor, Release, Stage, Build }; + enum CompareType : int { None = 0, Major=1, Minor=2, Release=3, Stage=4, Build=5, All = std::numeric_limits::max() }; + - ProjectVersion(QString version, CompareType c = Build); - ProjectVersion(const char * version, CompareType c = Build); + ProjectVersion(QString version, CompareType c = All); + ProjectVersion(const char * version, CompareType c = All); int getMajor() const { return m_major; } int getMinor() const { return m_minor; } - int getRelease() const { return m_release; } - QString getStage() const { return m_stage; } - int getBuild() const { return m_build; } + int getPatch() const { return m_patch; } + const QStringList& getLabels() const { return m_labels; } CompareType getCompareType() const { return m_compareType; } ProjectVersion setCompareType(CompareType compareType) { m_compareType = compareType; return * this; } @@ -54,11 +57,10 @@ class ProjectVersion private: QString m_version; - int m_major; - int m_minor; - int m_release; - QString m_stage; - int m_build; + int m_major = 0; + int m_minor = 0; + int m_patch = 0; + QStringList m_labels; CompareType m_compareType; } ; diff --git a/include/StepRecorder.h b/include/StepRecorder.h index b9653b1bbea..8240cc41a61 100644 --- a/include/StepRecorder.h +++ b/include/StepRecorder.h @@ -21,7 +21,7 @@ #ifndef STEP_RECORDER_H #define STEP_RECORDER_H -#include +#include #include #include #include @@ -130,7 +130,7 @@ class StepRecorder : public QObject private: bool m_pressed; - QTime releasedTimer; + QElapsedTimer releasedTimer; } ; QVector m_curStepNotes; // contains the current recorded step notes (i.e. while user still press the notes; before they are applied to the pattern) @@ -140,4 +140,4 @@ class StepRecorder : public QObject bool m_isStepInProgress = false; }; -#endif //STEP_RECORDER_H \ No newline at end of file +#endif //STEP_RECORDER_H diff --git a/include/SubWindow.h b/include/SubWindow.h index 148cf2c9997..55d05425ab8 100644 --- a/include/SubWindow.h +++ b/include/SubWindow.h @@ -55,7 +55,7 @@ class LMMS_EXPORT SubWindow : public QMdiSubWindow Q_PROPERTY( QColor borderColor READ borderColor WRITE setBorderColor ) public: - SubWindow( QWidget *parent = NULL, Qt::WindowFlags windowFlags = 0 ); + SubWindow( QWidget *parent = NULL, Qt::WindowFlags windowFlags = QFlag(0) ); // same as QWidet::normalGeometry, but works properly under X11 (see https://bugreports.qt.io/browse/QTBUG-256) QRect getTrueNormalGeometry() const; QBrush activeColor() const; diff --git a/include/lmms_math.h b/include/lmms_math.h index 7c68c6bfbf5..3fd76896e18 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -42,22 +42,41 @@ using namespace std; static inline float absFraction( const float _x ) { - return( _x - ( _x >= 0.0f ? floorf( _x ) : floorf( _x ) - 1 ) ); + return( _x - floorf( _x ) ); } static inline float fraction( const float _x ) { - return( _x - floorf( _x ) ); + return( _x - floorf( _x ) - ( _x >= 0.0f ? 0.0 : 1.0 ) ); } #else +/*! + * @brief Returns the wrapped fractional part of a float, a value between 0.0f and 1.0f. + * + * absFraction( 2.3) => 0.3 + * absFraction(-2.3) => 0.7 + * + * Note that this not the same as the absolute value of the fraction (as the function name suggests). + * If the result is interpreted as a phase of an oscillator, it makes that negative phases are + * converted to positive phases. + */ static inline float absFraction( const float _x ) { return( _x - ( _x >= 0.0f ? static_cast( _x ) : static_cast( _x ) - 1 ) ); } +/*! + * @brief Returns the fractional part of a float, a value between -1.0f and 1.0f. + * + * fraction( 2.3) => 0.3 + * fraction(-2.3) => -0.3 + * + * Note that if the return value is used as a phase of an oscillator, that the oscillator must support + * negative phases. + */ static inline float fraction( const float _x ) { return( _x - static_cast( _x ) ); diff --git a/plugins/Amplifier/Amplifier.cpp b/plugins/Amplifier/Amplifier.cpp index cc2a63304ff..e7250872f53 100644 --- a/plugins/Amplifier/Amplifier.cpp +++ b/plugins/Amplifier/Amplifier.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT amplifier_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Amplifier", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native amplifier plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native amplifier plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/BassBooster/BassBooster.cpp b/plugins/BassBooster/BassBooster.cpp index 535834aae77..1ed1f246712 100644 --- a/plugins/BassBooster/BassBooster.cpp +++ b/plugins/BassBooster/BassBooster.cpp @@ -34,7 +34,7 @@ Plugin::Descriptor PLUGIN_EXPORT bassbooster_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "BassBooster", - QT_TRANSLATE_NOOP( "pluginBrowser", "Boost your bass the fast and simple way" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Boost your bass the fast and simple way" ), "Tobias Doerffel ", 0x0100, Plugin::Effect, diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index 0580d47cd5e..fa19b195fc4 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitcrush_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Bitcrush", - QT_TRANSLATE_NOOP( "pluginBrowser", "An oversampling bitcrusher" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "An oversampling bitcrusher" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp index e2090980a06..78029134ebc 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.cpp +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -36,7 +36,7 @@ Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Crossover Equalizer", - QT_TRANSLATE_NOOP( "pluginBrowser", "A 4-band Crossover Equalizer" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A 4-band Crossover Equalizer" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/Delay/DelayEffect.cpp b/plugins/Delay/DelayEffect.cpp index 0daad0d9d8c..69ee924e8b0 100644 --- a/plugins/Delay/DelayEffect.cpp +++ b/plugins/Delay/DelayEffect.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT delay_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Delay", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native delay plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native delay plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index b4e11ccb396..d4e5d4b5ef0 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -36,7 +36,7 @@ Plugin::Descriptor PLUGIN_EXPORT dualfilter_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Dual Filter", - QT_TRANSLATE_NOOP( "pluginBrowser", "A Dual filter plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A Dual filter plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/DualFilter/DualFilterControlDialog.cpp b/plugins/DualFilter/DualFilterControlDialog.cpp index 73755bef38f..0c7732bc6c2 100755 --- a/plugins/DualFilter/DualFilterControlDialog.cpp +++ b/plugins/DualFilter/DualFilterControlDialog.cpp @@ -75,12 +75,12 @@ DualFilterControlDialog::DualFilterControlDialog( DualFilterControls* controls ) ToolTip::add( enabled2Toggle, tr( "Enable/disable filter 2" ) ); ComboBox * m_filter1ComboBox = new ComboBox( this ); - m_filter1ComboBox->setGeometry( 19, 70, 137, 22 ); + m_filter1ComboBox->setGeometry( 19, 70, 137, ComboBox::DEFAULT_HEIGHT ); m_filter1ComboBox->setFont( pointSize<8>( m_filter1ComboBox->font() ) ); m_filter1ComboBox->setModel( &controls->m_filter1Model ); ComboBox * m_filter2ComboBox = new ComboBox( this ); - m_filter2ComboBox->setGeometry( 217, 70, 137, 22 ); + m_filter2ComboBox->setGeometry( 217, 70, 137, ComboBox::DEFAULT_HEIGHT ); m_filter2ComboBox->setFont( pointSize<8>( m_filter2ComboBox->font() ) ); m_filter2ComboBox->setModel( &controls->m_filter2Model ); } diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index 6b7c61bcc49..a295374931c 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -39,7 +39,7 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Equalizer", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native eq plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native eq plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/Flanger/FlangerEffect.cpp b/plugins/Flanger/FlangerEffect.cpp index 7e441cf62a3..65b58ce49ed 100644 --- a/plugins/Flanger/FlangerEffect.cpp +++ b/plugins/Flanger/FlangerEffect.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT flanger_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Flanger", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native flanger plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native flanger plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index 2f787044f25..95ba5a215b5 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -53,7 +53,7 @@ Plugin::Descriptor PLUGIN_EXPORT freeboy_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "FreeBoy", - QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of GameBoy (TM) APU" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Emulation of GameBoy (TM) APU" ), "Attila Herman " "Csaba Hruska ", diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 334e2bd7737..1e077f5d4e3 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -63,7 +63,7 @@ Plugin::Descriptor PLUGIN_EXPORT gigplayer_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "GIG Player", - QT_TRANSLATE_NOOP( "pluginBrowser", "Player for GIG files" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Player for GIG files" ), "Garrett Wilson ", 0x0100, Plugin::Instrument, diff --git a/plugins/GigPlayer/PatchesDialog.h b/plugins/GigPlayer/PatchesDialog.h index 0836631acad..ae00f660aaf 100644 --- a/plugins/GigPlayer/PatchesDialog.h +++ b/plugins/GigPlayer/PatchesDialog.h @@ -43,7 +43,7 @@ class PatchesDialog : public QDialog, private Ui::PatchesDialog public: // Constructor. - PatchesDialog( QWidget * pParent = 0, Qt::WindowFlags wflags = 0 ); + PatchesDialog(QWidget * pParent = 0, Qt::WindowFlags wflags = QFlag(0)); // Destructor. virtual ~PatchesDialog(); diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp index 4ed0a89a3f1..4a69bc4511c 100644 --- a/plugins/HydrogenImport/HydrogenImport.cpp +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -29,7 +29,7 @@ Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Hydrogen Import", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for importing Hydrogen files into LMMS" ), "frank mather", 0x0100, diff --git a/plugins/LadspaEffect/LadspaEffect.cpp b/plugins/LadspaEffect/LadspaEffect.cpp index d7e66bf47aa..117e1e0e10e 100644 --- a/plugins/LadspaEffect/LadspaEffect.cpp +++ b/plugins/LadspaEffect/LadspaEffect.cpp @@ -53,7 +53,7 @@ Plugin::Descriptor PLUGIN_EXPORT ladspaeffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LADSPA", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for using arbitrary LADSPA-effects " "inside LMMS." ), "Danny McRae ", diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 2ee5ceabbe5..36e9df46ba7 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -39,7 +39,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("pluginBrowser", + QT_TRANSLATE_NOOP("PluginBrowser", "plugin for using arbitrary LV2-effects inside LMMS."), "Johannes Lorenz ", 0x0100, diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 974aaf416b4..0f7534ac8c9 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("pluginBrowser", + QT_TRANSLATE_NOOP("PluginBrowser", "plugin for using arbitrary LV2 instruments inside LMMS."), "Johannes Lorenz ", 0x0100, diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp index 1860527c10e..466664b167d 100644 --- a/plugins/MidiExport/MidiExport.cpp +++ b/plugins/MidiExport/MidiExport.cpp @@ -47,7 +47,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiexport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "MIDI Export", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for exporting MIDI-files from LMMS" ), "Mohamed Abdel Maksoud and " "Hyunjin Song ", diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 3646975a29a..8b2aa6117d6 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -31,6 +31,7 @@ #include #include +#include #include "MidiImport.h" #include "TrackContainer.h" @@ -64,7 +65,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiimport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "MIDI Import", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for importing MIDI-files into LMMS" ), "Tobias Doerffel ", 0x0100, @@ -305,7 +306,7 @@ class smfMidiChannel bool MidiImport::readSMF( TrackContainer* tc ) { - + const int MIDI_CC_COUNT = 128 + 1; // 0-127 (128) + pitch bend const int preTrackSteps = 2; QProgressDialog pd( TrackContainer::tr( "Importing MIDI-file..." ), TrackContainer::tr( "Cancel" ), 0, preTrackSteps, gui->mainWindow() ); @@ -315,10 +316,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) pd.setValue( 0 ); - std::stringstream stream; - QByteArray arr = readAllData(); - stream.str(std::string(arr.constData(), arr.size())); - + std::istringstream stream(readAllData().toStdString()); Alg_seq_ptr seq = new Alg_seq(stream, true); seq->convert_to_beats(); @@ -326,8 +324,12 @@ bool MidiImport::readSMF( TrackContainer* tc ) pd.setValue( 1 ); // 128 CC + Pitch Bend - smfMidiCC ccs[129]; - smfMidiChannel chs[256]; + smfMidiCC ccs[MIDI_CC_COUNT]; + + // channels can be set out of 256 range + // using unordered_map should fix most invalid loads and crashes while loading + std::unordered_map chs; + // NOTE: unordered_map::operator[] creates a new element if none exists MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel(); AutomationTrack * nt = dynamic_cast( @@ -407,7 +409,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) Alg_track_ptr trk = seq->track( t ); pd.setValue( t + preTrackSteps ); - for( int c = 0; c < 129; c++ ) + for( int c = 0; c < MIDI_CC_COUNT; c++ ) { ccs[c].clear(); } @@ -423,7 +425,10 @@ bool MidiImport::readSMF( TrackContainer* tc ) if( evt->is_update() ) { QString attr = evt->get_attribute(); - if( attr == "tracknames" && evt->get_update_type() == 's' ) { + // seqnames is a track0 identifier (see allegro code) + if (attr == (t == 0 ? "seqnames" : "tracknames") + && evt->get_update_type() == 's') + { trackName = evt->get_string_value(); handled = true; } @@ -444,7 +449,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) printf( "\n" ); } } - else if( evt->is_note() && evt->chan < 256 ) + else if (evt->is_note()) { smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); Alg_note_ptr noteEvt = dynamic_cast( evt ); @@ -558,28 +563,26 @@ bool MidiImport::readSMF( TrackContainer* tc ) delete seq; - for( int c=0; c < 256; ++c ) + for( auto& c: chs ) { - if (chs[c].hasNotes) + if (c.second.hasNotes) { - chs[c].splitPatterns(); + c.second.splitPatterns(); } - else if (chs[c].it) + else if (c.second.it) { printf(" Should remove empty track\n"); // must delete trackView first - but where is it? //tc->removeTrack( chs[c].it ); //it->deleteLater(); } - } - - // Set channel 10 to drums as per General MIDI's orders - if( chs[9].hasNotes && chs[9].it_inst && chs[9].isSF2 ) - { - // AFAIK, 128 should be the standard bank for drums in SF2. - // If not, this has to be made configurable. - chs[9].it_inst->childModel( "bank" )->setValue( 128 ); - chs[9].it_inst->childModel( "patch" )->setValue( 0 ); + // Set channel 10 to drums as per General MIDI's orders + if (c.first % 16l == 9 /* channel 10 */ + && c.second.hasNotes && c.second.it_inst && c.second.isSF2) + { + c.second.it_inst->childModel("bank")->setValue(128); + c.second.it_inst->childModel("patch")->setValue(0); + } } return true; diff --git a/plugins/MidiImport/portsmf/algrd_internal.h b/plugins/MidiImport/portsmf/algrd_internal.h index 3b77adc4cd2..7e3ac88522b 100644 --- a/plugins/MidiImport/portsmf/algrd_internal.h +++ b/plugins/MidiImport/portsmf/algrd_internal.h @@ -1,4 +1,5 @@ -/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */ - -Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq); - +/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */ + +Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq, + double *offset_ptr = NULL); + diff --git a/plugins/MidiImport/portsmf/allegro.cpp b/plugins/MidiImport/portsmf/allegro.cpp index 3f7b84073a5..5664e910eca 100644 --- a/plugins/MidiImport/portsmf/allegro.cpp +++ b/plugins/MidiImport/portsmf/allegro.cpp @@ -9,7 +9,7 @@ 04 apr 03 -- fixed bug in add_track that caused infinite loop */ -#include "debug.h" +#include "assert.h" #include "stdlib.h" #include "stdio.h" #include "string.h" @@ -25,13 +25,15 @@ using namespace std; #define STREQL(x, y) (strcmp(x, y) == 0) #define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define ROUND(x) ((int) ((x) + 0.5)) // 4311 is type cast ponter to long warning // 4996 is warning against strcpy // 4267 is size_t to long warning //#pragma warning(disable: 4311 4996 4267) Alg_atoms symbol_table; -Serial_buffer Alg_track::ser_buf; // declare the static variable +Serial_read_buffer Alg_track::ser_read_buf; // declare the static variables +Serial_write_buffer Alg_track::ser_write_buf; bool within(double d1, double d2, double epsilon) { @@ -52,12 +54,10 @@ void Alg_atoms::expand() { maxlen = (maxlen + 5); // extra growth for small sizes maxlen += (maxlen >> 2); // add 25% - char **new_atoms = new Alg_attribute[maxlen]; + Alg_attribute *new_atoms = new Alg_attribute[maxlen]; // now do copy - if (atoms) { - memcpy(new_atoms, atoms, len * sizeof(Alg_attribute)); - delete[] atoms; - } + memcpy(new_atoms, atoms, len * sizeof(Alg_attribute)); + if (atoms) delete[] atoms; atoms = new_atoms; } @@ -81,6 +81,7 @@ Alg_attribute Alg_atoms::insert_new(const char *name, char attr_type) Alg_attribute Alg_atoms::insert_attribute(Alg_attribute attr) { + // should use hash algorithm for (int i = 0; i < len; i++) { if (STREQL(attr, atoms[i])) { return atoms[i]; @@ -123,7 +124,7 @@ void Alg_parameter::show() printf("%s:%s", attr_name(), s); break; case 'i': - printf("%s:%d", attr_name(), (int) i); + printf("%s:%ld", attr_name(), i); break; case 'l': printf("%s:%s", attr_name(), (l ? "t" : "f")); @@ -143,7 +144,8 @@ Alg_parameter::~Alg_parameter() } -void Alg_parameters::insert_real(Alg_parameters **list, char *name, double r) +void Alg_parameters::insert_real(Alg_parameters **list, const char *name, + double r) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -153,7 +155,8 @@ void Alg_parameters::insert_real(Alg_parameters **list, char *name, double r) } -void Alg_parameters::insert_string(Alg_parameters **list, char *name, char *s) +void Alg_parameters::insert_string(Alg_parameters **list, const char *name, + const char *s) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -164,7 +167,8 @@ void Alg_parameters::insert_string(Alg_parameters **list, char *name, char *s) } -void Alg_parameters::insert_integer(Alg_parameters **list, char *name, long i) +void Alg_parameters::insert_integer(Alg_parameters **list, const char *name, + long i) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -174,7 +178,8 @@ void Alg_parameters::insert_integer(Alg_parameters **list, char *name, long i) } -void Alg_parameters::insert_logical(Alg_parameters **list, char *name, bool l) +void Alg_parameters::insert_logical(Alg_parameters **list, const char *name, + bool l) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -184,7 +189,8 @@ void Alg_parameters::insert_logical(Alg_parameters **list, char *name, bool l) } -void Alg_parameters::insert_atom(Alg_parameters **list, char *name, char *s) +void Alg_parameters::insert_atom(Alg_parameters **list, const char *name, + const char *s) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -194,7 +200,8 @@ void Alg_parameters::insert_atom(Alg_parameters **list, char *name, char *s) } -Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, const char *name) +Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, + const char *name) { while (*list) { if (STREQL((*list)->parm.attr_name(), name)) { @@ -209,12 +216,12 @@ Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, const char *na } -Alg_parameter_ptr Alg_parameters::find(Alg_attribute *attr) +Alg_parameter_ptr Alg_parameters::find(Alg_attribute attr) { assert(attr); Alg_parameters_ptr temp = this; while (temp) { - if (temp->parm.attr == *attr) { + if (temp->parm.attr == attr) { return &(temp->parm); } } @@ -226,9 +233,9 @@ int Alg_event::get_type_code() { if (!is_note()) { const char* attr = get_attribute(); - if (STREQL(attr, "gate")) // volume change + if (STREQL(attr, "gater")) // volume change return ALG_GATE; - if (STREQL(attr, "bend")) // pitch bend + if (STREQL(attr, "bendr")) // pitch bend return ALG_BEND; if (strncmp(attr, "control", 7) == 0) // control change // note that midi control changes have attributes of the form @@ -237,15 +244,15 @@ int Alg_event::get_type_code() // We don't check for decimal numbers in the range 0-127, so any // attribute that begins with "control" is an ALG_CONTROL: return ALG_CONTROL; - if (STREQL(attr, "program")) // program change + if (STREQL(attr, "programi")) // program change return ALG_PROGRAM; - if (STREQL(attr, "pressure")) // pressure change + if (STREQL(attr, "pressurer")) // pressure change return ALG_PRESSURE; - if (STREQL(attr, "keysig")) // key signature + if (STREQL(attr, "keysigi")) // key signature return ALG_KEYSIG; - if (STREQL(attr, "timesig_num")) // time signature numerator + if (STREQL(attr, "timesig_numi")) // time signature numerator return ALG_TIMESIG_NUM; - if (STREQL(attr, "timesig_den")) // time signature denominator + if (STREQL(attr, "timesig_deni")) // time signature denominator return ALG_TIMESIG_DEN; return ALG_OTHER; } @@ -258,7 +265,7 @@ void Alg_event::set_parameter(Alg_parameter_ptr new_parameter) Alg_parameter_ptr parm; if (is_note()) { Alg_note_ptr note = (Alg_note_ptr) this; - parm = note->parameters->find(&(new_parameter->attr)); + parm = note->parameters->find(new_parameter->attr); if (!parm) { note->parameters = new Alg_parameters(note->parameters); parm = &(note->parameters->parm); @@ -271,7 +278,7 @@ void Alg_event::set_parameter(Alg_parameter_ptr new_parameter) } -void Alg_event::set_string_value(char *a, char *value) +void Alg_event::set_string_value(const char *a, const char *value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -284,7 +291,7 @@ void Alg_event::set_string_value(char *a, char *value) } -void Alg_event::set_real_value(char *a, double value) +void Alg_event::set_real_value(const char *a, double value) { assert(a); // must be non-null // attr is like a, but it has the type code prefixed for @@ -300,7 +307,7 @@ void Alg_event::set_real_value(char *a, double value) } -void Alg_event::set_logical_value(char *a, bool value) +void Alg_event::set_logical_value(const char *a, bool value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -313,7 +320,7 @@ void Alg_event::set_logical_value(char *a, bool value) } -void Alg_event::set_integer_value(char *a, long value) +void Alg_event::set_integer_value(const char *a, long value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -326,7 +333,7 @@ void Alg_event::set_integer_value(char *a, long value) } -void Alg_event::set_atom_value(char *a, char *value) +void Alg_event::set_atom_value(const char *a, const char *value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -402,18 +409,18 @@ void Alg_event::set_duration(double d) } -bool Alg_event::has_attribute(char *a) +bool Alg_event::has_attribute(const char *a) { assert(is_note()); assert(a); // must be non-null Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); return parm != NULL; } -char Alg_event::get_attribute_type(char *a) +char Alg_event::get_attribute_type(const char *a) { assert(is_note()); assert(a); @@ -421,66 +428,66 @@ char Alg_event::get_attribute_type(char *a) } -char *Alg_event::get_string_value(char *a, char *value) +const char *Alg_event::get_string_value(const char *a, const char *value) { assert(is_note()); assert(a); // must be non-null Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 's'); // must be of type string - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->s; return value; } -double Alg_event::get_real_value(char *a, double value) +double Alg_event::get_real_value(const char *a, double value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'r'); // must be of type real - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->r; return value; } -bool Alg_event::get_logical_value(char *a, bool value) +bool Alg_event::get_logical_value(const char *a, bool value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'l'); // must be of type logical - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->l; return value; } -long Alg_event::get_integer_value(char *a, long value) +long Alg_event::get_integer_value(const char *a, long value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'i'); // must be of type integer - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->i; return value; } -char *Alg_event::get_atom_value(char *a, char *value) +const char *Alg_event::get_atom_value(const char *a, const char *value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'a'); // must be of type atom - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->a; // if default is a string, convert to an atom (unique // string in symbol table) and return it @@ -489,7 +496,7 @@ char *Alg_event::get_atom_value(char *a, char *value) } -void Alg_event::delete_attribute(char *a) +void Alg_event::delete_attribute(const char *a) { assert(is_note()); Alg_note* note = (Alg_note *) this; @@ -514,12 +521,12 @@ char Alg_event::get_update_type() } -char *Alg_event::get_string_value() +const char *Alg_event::get_string_value() { assert(is_update()); Alg_update* update = (Alg_update *) this; assert(get_update_type() == 's'); - return update->parameter.a; + return update->parameter.s; } @@ -550,7 +557,7 @@ long Alg_event::get_integer_value() } -char *Alg_event::get_atom_value() +const char *Alg_event::get_atom_value() { assert(is_update()); Alg_update* update = (Alg_update *) this; @@ -566,7 +573,7 @@ bool Alg_event::overlap(double t, double len, bool all) return true; if (all && is_note()) { double dur = ((Alg_note_ptr) this)->dur; - // note ends within region + // note overlaps with region if (time < t && time + dur - ALG_EPS > t) return true; } @@ -601,9 +608,9 @@ Alg_note::~Alg_note() void Alg_note::show() { - printf("Alg_note: time %g, chan %d, dur %g, key %d, " + printf("Alg_note: time %g, chan %ld, dur %g, key %ld, " "pitch %g, loud %g, attributes ", - time, (int) chan, dur, (int) key, pitch, loud); + time, chan, dur, key, pitch, loud); Alg_parameters_ptr parms = parameters; while (parms) { parms->parm.show(); @@ -670,6 +677,8 @@ Alg_event_ptr Alg_events::uninsert(long index) { assert(0 <= index && index < len); Alg_event_ptr event = events[index]; + //printf("memmove: %x from %x (%d)\n", events + index, events + index + 1, + // sizeof(Alg_event_ptr) * (len - index - 1)); memmove(events + index, events + index + 1, sizeof(Alg_event_ptr) * (len - index - 1)); len--; @@ -695,6 +704,7 @@ void Alg_events::append(Alg_event_ptr event) Alg_events::~Alg_events() { + assert(!in_use); // individual events are not deleted, only the array if (events) { delete[] events; @@ -710,7 +720,7 @@ Alg_event_list::Alg_event_list(Alg_track *owner) } -Alg_event_ptr &Alg_event_list::operator [](int i) +Alg_event_ptr const &Alg_event_list::operator [](int i) { assert(i >= 0 && i < len); return events[i]; @@ -729,8 +739,8 @@ void Alg_event_list::set_start_time(Alg_event *event, double t) // For Alg_track, change the time and move the event to the right place // For Alg_seq, find the track and do the update there - long index = 0, i; - Alg_track_ptr track_ptr = Alg_track_ptr(); + long index = 0, i; + Alg_track_ptr track_ptr = nullptr; if (type == 'e') { // this is an Alg_event_list // make sure the owner has not changed its event set assert(events_owner && @@ -847,7 +857,12 @@ double Alg_time_map::beat_to_time(double beat) return beat; } int i = locate_beat(beat); - if (i == beats.len) { + // case 1: beat is between two time/beat pairs + if (0 < i && i < beats.len) { + mbi = &beats[i - 1]; + mbi1 = &beats[i]; + // case 2: beat is beyond last time/beat pair + } else if (i == beats.len) { if (last_tempo_flag) { return beats[i - 1].time + (beat - beats[i - 1].beat) / last_tempo; @@ -858,11 +873,11 @@ double Alg_time_map::beat_to_time(double beat) mbi = &beats[i - 2]; mbi1 = &beats[i - 1]; } - } else { - mbi = &beats[i - 1]; - mbi1 = &beats[i]; + // case 3: beat is at time 0 + } else /* if (i == 0) */ { + return beats[0].time; } - // whether w extrapolate or interpolate, the math is the same + // whether we extrapolate or interpolate, the math is the same double time_dif = mbi1->time - mbi->time; double beat_dif = mbi1->beat - mbi->beat; return mbi->time + (beat - mbi->beat) * time_dif / beat_dif; @@ -945,6 +960,7 @@ bool Alg_time_map::insert_tempo(double tempo, double beat) // compute difference too diff = diff - old_diff; // apply new_diff to score and beats + i++; while (i < beats.len) { beats[i].time = beats[i].time + diff; i++; @@ -954,6 +970,38 @@ bool Alg_time_map::insert_tempo(double tempo, double beat) } +double Alg_time_map::get_tempo(double beat) +{ + Alg_beat_ptr mbi; + Alg_beat_ptr mbi1; + // if beat < 0, there is probably an error; return something nice anyway + if (beat < 0) return ALG_DEFAULT_BPM / 60.0; + long i = locate_beat(beat); + // this code is similar to beat_to_time() so far, but we want to get + // beyond beat if possible because we want the tempo FOLLOWING beat + // (Consider the case beat == 0.0) + if (i < beats.len && beat >= beats[i].beat) i++; + // case 1: beat is between two time/beat pairs + if (i < beats.len) { + mbi = &beats[i - 1]; + mbi1 = &beats[i]; + // case 2: beat is beyond last time/beat pair + } else /* if (i == beats.len) */ { + if (last_tempo_flag) { + return last_tempo; + } else if (i == 1) { + return ALG_DEFAULT_BPM / 60.0; + } else { + mbi = &beats[i - 2]; + mbi1 = &beats[i - 1]; + } + } + double time_dif = mbi1->time - mbi->time; + double beat_dif = mbi1->beat - mbi->beat; + return beat_dif / time_dif; +} + + bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat) { if (start_beat >= end_beat) return false; @@ -975,6 +1023,34 @@ bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat) } +bool Alg_time_map::stretch_region(double b0, double b1, double dur) +{ + // find current duration + double t0 = beat_to_time(b0); + double t1 = beat_to_time(b1); + double old_dur = t1 - t0; + if (old_dur <= 0 || dur <= 0) return false; + double scale = dur / old_dur; // larger scale => slower + // insert a beat if necessary at b0 and b1 + insert_beat(t0, b0); + insert_beat(t1, b1); + long start_x = locate_beat(b0); + long stop_x = locate_beat(b1); + double orig_time = beats[start_x].time; + double prev_time = orig_time; + for (int i = start_x + 1; i < beats.len; i++) { + double delta = beats[i].time - orig_time; + if (i <= stop_x) { // change tempo to next Alg_beat + delta *= scale; + } + orig_time = beats[i].time; + prev_time += delta; + beats[i].time = prev_time; + } + return true; +} + + void Alg_time_map::trim(double start, double end, bool units_are_seconds) { // extract the time map from start to end and shift to time zero @@ -1277,22 +1353,22 @@ void Alg_track::serialize(void **buffer, long *bytes) // // The format for a track is given within the Seq format above assert(get_type() == 't'); - ser_buf.init_for_write(); + ser_write_buf.init_for_write(); serialize_track(); - *buffer = ser_buf.to_heap(bytes); + *buffer = ser_write_buf.to_heap(bytes); } void Alg_seq::serialize(void **buffer, long *bytes) { assert(get_type() == 's'); - ser_buf.init_for_write(); + ser_write_buf.init_for_write(); serialize_seq(); - *buffer = ser_buf.to_heap(bytes); + *buffer = ser_write_buf.to_heap(bytes); } -void Serial_buffer::check_buffer(long needed) +void Serial_write_buffer::check_buffer(long needed) { if (len < (ptr - buffer) + needed) { // do we need more space? long new_len = len * 2; // exponential growth is important @@ -1301,9 +1377,11 @@ void Serial_buffer::check_buffer(long needed) // make sure new_len is as big as needed if (needed > new_len) new_len = needed; char *new_buffer = new char[new_len]; // allocate space - memcpy(new_buffer, buffer, len); // copy from old buffer ptr = new_buffer + (ptr - buffer); // relocate ptr to new buffer - delete buffer; // free old buffer + if (len > 0) { // we had a buffer already + memcpy(new_buffer, buffer, len); // copy from old buffer + delete buffer; // free old buffer + } buffer = new_buffer; // update buffer information len = new_len; } @@ -1315,37 +1393,39 @@ void Alg_seq::serialize_seq() int i; // loop counters // we can easily compute how much buffer space we need until we // get to tracks, so expand at least that much - long needed = 48 + 16 * time_map->beats.len + 24 * time_sig.length(); - ser_buf.check_buffer(needed); - ser_buf.set_char('A'); - ser_buf.set_char('L'); - ser_buf.set_char('G'); - ser_buf.set_char('S'); - long length_offset = ser_buf.get_posn(); - ser_buf.set_int32(0); // leave room to come back and write length - ser_buf.set_int32(channel_offset_per_track); - ser_buf.set_int32(units_are_seconds); - ser_buf.set_double(time_map->last_tempo); - ser_buf.set_int32(time_map->last_tempo_flag); - ser_buf.set_int32(time_map->beats.len); + long needed = 64 + 16 * time_map->beats.len + 24 * time_sig.length(); + ser_write_buf.check_buffer(needed); + ser_write_buf.set_char('A'); + ser_write_buf.set_char('L'); + ser_write_buf.set_char('G'); + ser_write_buf.set_char('S'); + long length_offset = ser_write_buf.get_posn(); + ser_write_buf.set_int32(0); // leave room to come back and write length + ser_write_buf.set_int32(channel_offset_per_track); + ser_write_buf.set_int32(units_are_seconds); + ser_write_buf.set_double(beat_dur); + ser_write_buf.set_double(real_dur); + ser_write_buf.set_double(time_map->last_tempo); + ser_write_buf.set_int32(time_map->last_tempo_flag); + ser_write_buf.set_int32(time_map->beats.len); for (i = 0; i < time_map->beats.len; i++) { - ser_buf.set_double(time_map->beats[i].time); - ser_buf.set_double(time_map->beats[i].beat); + ser_write_buf.set_double(time_map->beats[i].time); + ser_write_buf.set_double(time_map->beats[i].beat); } - ser_buf.set_int32(time_sig.length()); - ser_buf.pad(); + ser_write_buf.set_int32(time_sig.length()); + ser_write_buf.pad(); for (i = 0; i < time_sig.length(); i++) { - ser_buf.set_double(time_sig[i].beat); - ser_buf.set_double(time_sig[i].num); - ser_buf.set_double(time_sig[i].den); + ser_write_buf.set_double(time_sig[i].beat); + ser_write_buf.set_double(time_sig[i].num); + ser_write_buf.set_double(time_sig[i].den); } - ser_buf.set_int32(tracks()); - ser_buf.pad(); + ser_write_buf.set_int32(tracks()); + ser_write_buf.pad(); for (i = 0; i < tracks(); i++) { track(i)->serialize_track(); } // do not include ALGS, include padding at end - ser_buf.store_long(length_offset, ser_buf.get_posn() - length_offset); + ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset); } @@ -1353,51 +1433,51 @@ void Alg_track::serialize_track() { // to simplify the code, copy from parameter addresses to locals int j; - ser_buf.check_buffer(32); - ser_buf.set_char('A'); - ser_buf.set_char('L'); - ser_buf.set_char('G'); - ser_buf.set_char('T'); - long length_offset = ser_buf.get_posn(); // save location for track length - ser_buf.set_int32(0); // room to write track length - ser_buf.set_int32(units_are_seconds); - ser_buf.set_double(beat_dur); - ser_buf.set_double(real_dur); - ser_buf.set_int32(len); + ser_write_buf.check_buffer(32); + ser_write_buf.set_char('A'); + ser_write_buf.set_char('L'); + ser_write_buf.set_char('G'); + ser_write_buf.set_char('T'); + long length_offset = ser_write_buf.get_posn(); // save location for track length + ser_write_buf.set_int32(0); // room to write track length + ser_write_buf.set_int32(units_are_seconds); + ser_write_buf.set_double(beat_dur); + ser_write_buf.set_double(real_dur); + ser_write_buf.set_int32(len); for (j = 0; j < len; j++) { - ser_buf.check_buffer(24); + ser_write_buf.check_buffer(24); Alg_event *event = (*this)[j]; - ser_buf.set_int32(event->get_selected()); - ser_buf.set_int32(event->get_type()); - ser_buf.set_int32(event->get_identifier()); - ser_buf.set_int32(event->chan); - ser_buf.set_double(event->time); + ser_write_buf.set_int32(event->get_selected()); + ser_write_buf.set_int32(event->get_type()); + ser_write_buf.set_int32(event->get_identifier()); + ser_write_buf.set_int32(event->chan); + ser_write_buf.set_double(event->time); if (event->is_note()) { - ser_buf.check_buffer(20); + ser_write_buf.check_buffer(20); Alg_note *note = (Alg_note *) event; - ser_buf.set_float(note->pitch); - ser_buf.set_float(note->loud); - ser_buf.set_double(note->dur); - long parm_num_offset = ser_buf.get_posn(); + ser_write_buf.set_float(note->pitch); + ser_write_buf.set_float(note->loud); + ser_write_buf.set_double(note->dur); + long parm_num_offset = ser_write_buf.get_posn(); long parm_num = 0; - ser_buf.set_int32(0); // placeholder for no. parameters + ser_write_buf.set_int32(0); // placeholder for no. parameters Alg_parameters_ptr parms = note->parameters; while (parms) { serialize_parameter(&(parms->parm)); parms = parms->next; parm_num++; } - ser_buf.store_long(parm_num_offset, parm_num); + ser_write_buf.store_long(parm_num_offset, parm_num); } else { assert(event->is_update()); Alg_update *update = (Alg_update *) event; serialize_parameter(&(update->parameter)); } - ser_buf.check_buffer(7); // maximum padding possible - ser_buf.pad(); + ser_write_buf.check_buffer(7); // maximum padding possible + ser_write_buf.pad(); } // write length, not including ALGT, including padding at end - ser_buf.store_long(length_offset, ser_buf.get_posn() - length_offset); + ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset); } @@ -1406,29 +1486,29 @@ void Alg_track::serialize_parameter(Alg_parameter *parm) // add eight to account for name + zero end-of-string and the // possibility of adding 7 padding bytes long len = strlen(parm->attr_name()) + 8; - ser_buf.check_buffer(len); - ser_buf.set_string(parm->attr_name()); - ser_buf.pad(); + ser_write_buf.check_buffer(len); + ser_write_buf.set_string(parm->attr_name()); + ser_write_buf.pad(); switch (parm->attr_type()) { case 'r': - ser_buf.check_buffer(8); - ser_buf.set_double(parm->r); + ser_write_buf.check_buffer(8); + ser_write_buf.set_double(parm->r); break; case 's': - ser_buf.check_buffer(strlen(parm->s) + 1); - ser_buf.set_string(parm->s); + ser_write_buf.check_buffer(strlen(parm->s) + 1); + ser_write_buf.set_string(parm->s); break; case 'i': - ser_buf.check_buffer(4); - ser_buf.set_int32(parm->i); + ser_write_buf.check_buffer(4); + ser_write_buf.set_int32(parm->i); break; case 'l': - ser_buf.check_buffer(4); - ser_buf.set_int32(parm->l); + ser_write_buf.check_buffer(4); + ser_write_buf.set_int32(parm->l); break; case 'a': - ser_buf.check_buffer(strlen(parm->a) + 1); - ser_buf.set_string(parm->a); + ser_write_buf.check_buffer(strlen(parm->a) + 1); + ser_write_buf.set_string(parm->a); break; } } @@ -1438,55 +1518,69 @@ void Alg_track::serialize_parameter(Alg_parameter *parm) Alg_track *Alg_track::unserialize(void *buffer, long len) { assert(len > 8); - ser_buf.init_for_read(buffer, len); - bool alg = ser_buf.get_char() == 'A' && - ser_buf.get_char() == 'L' && - ser_buf.get_char() == 'G'; - assert(alg); - char c = ser_buf.get_char(); + ser_read_buf.init_for_read(buffer, len); + bool alg = ser_read_buf.get_char() == 'A' && + ser_read_buf.get_char() == 'L' && + ser_read_buf.get_char() == 'G'; + assert(alg); (void)alg; // unused variable + char c = ser_read_buf.get_char(); if (c == 'S') { Alg_seq *seq = new Alg_seq; + ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,S seq->unserialize_seq(); return seq; } else { assert(c == 'T'); Alg_track *track = new Alg_track; + ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,T track->unserialize_track(); return track; } } +//#pragma warning(disable: 4800) // long to bool performance warning + +/* Note: this Alg_seq must have a default initialized Alg_time_map. + * It will be filled in with data from the ser_read_buf buffer. + */ void Alg_seq::unserialize_seq() { - ser_buf.check_input_buffer(28); - long len = ser_buf.get_int32(); - assert(ser_buf.get_len() >= len); - channel_offset_per_track = ser_buf.get_int32(); - units_are_seconds = (bool) ser_buf.get_int32(); - time_map = new Alg_time_map(); - time_map->last_tempo = ser_buf.get_double(); - time_map->last_tempo_flag = (bool) ser_buf.get_int32(); - long beats = ser_buf.get_int32(); - ser_buf.check_input_buffer(beats * 16 + 4); + ser_read_buf.check_input_buffer(48); + bool algs = (ser_read_buf.get_char() == 'A') && + (ser_read_buf.get_char() == 'L') && + (ser_read_buf.get_char() == 'G') && + (ser_read_buf.get_char() == 'S'); + assert(algs); (void)algs; // unused variable + long len = ser_read_buf.get_int32(); + assert(ser_read_buf.get_len() >= len); (void)len; // unused variable + channel_offset_per_track = ser_read_buf.get_int32(); + units_are_seconds = ser_read_buf.get_int32() != 0; + beat_dur = ser_read_buf.get_double(); + real_dur = ser_read_buf.get_double(); + // no need to allocate an Alg_time_map since it's done during initialization + time_map->last_tempo = ser_read_buf.get_double(); + time_map->last_tempo_flag = ser_read_buf.get_int32() != 0; + long beats = ser_read_buf.get_int32(); + ser_read_buf.check_input_buffer(beats * 16 + 4); int i; for (i = 0; i < beats; i++) { - double time = ser_buf.get_double(); - double beat = ser_buf.get_double(); + double time = ser_read_buf.get_double(); + double beat = ser_read_buf.get_double(); time_map->insert_beat(time, beat); // printf("time_map: %g, %g\n", time, beat); } - long time_sig_len = ser_buf.get_int32(); - ser_buf.get_pad(); - ser_buf.check_input_buffer(time_sig_len * 24 + 8); + long time_sig_len = ser_read_buf.get_int32(); + ser_read_buf.get_pad(); + ser_read_buf.check_input_buffer(time_sig_len * 24 + 8); for (i = 0; i < time_sig_len; i++) { - double beat = ser_buf.get_double(); - double num = ser_buf.get_double(); - double den = ser_buf.get_double(); + double beat = ser_read_buf.get_double(); + double num = ser_read_buf.get_double(); + double den = ser_read_buf.get_double(); time_sig.insert(beat, num, den); } - long tracks_num = ser_buf.get_int32(); - ser_buf.get_pad(); + long tracks_num = ser_read_buf.get_int32(); + ser_read_buf.get_pad(); add_track(tracks_num - 1); // create tracks_num tracks for (i = 0; i < tracks_num; i++) { track(i)->unserialize_track(); @@ -1494,40 +1588,41 @@ void Alg_seq::unserialize_seq() // assume seq started at beginning of buffer. len measures // bytes after 'ALGS' header, so add 4 bytes and compare to // current buffer position -- they should agree - assert(ser_buf.get_posn() == len + 4); + assert(ser_read_buf.get_posn() == len + 4); } void Alg_track::unserialize_track() { - ser_buf.check_input_buffer(32); - assert(ser_buf.get_char() == 'A'); - assert(ser_buf.get_char() == 'L'); - assert(ser_buf.get_char() == 'G'); - assert(ser_buf.get_char() == 'T'); - long offset = ser_buf.get_posn(); // stored length does not include 'ALGT' - long bytes = ser_buf.get_int32(); - assert(bytes <= ser_buf.get_len() - offset); - units_are_seconds = (bool) ser_buf.get_int32(); - beat_dur = ser_buf.get_double(); - real_dur = ser_buf.get_double(); - int event_count = ser_buf.get_int32(); + ser_read_buf.check_input_buffer(32); + bool algt = (ser_read_buf.get_char() == 'A') && + (ser_read_buf.get_char() == 'L') && + (ser_read_buf.get_char() == 'G') && + (ser_read_buf.get_char() == 'T'); + assert(algt); (void)algt; // unused variable + long offset = ser_read_buf.get_posn(); // stored length does not include 'ALGT' + long bytes = ser_read_buf.get_int32(); + assert(bytes <= ser_read_buf.get_len() - offset); (void)offset; (void)bytes; // unused variable + units_are_seconds = (bool) ser_read_buf.get_int32(); + beat_dur = ser_read_buf.get_double(); + real_dur = ser_read_buf.get_double(); + int event_count = ser_read_buf.get_int32(); for (int i = 0; i < event_count; i++) { - ser_buf.check_input_buffer(24); - long selected = ser_buf.get_int32(); - char type = (char) ser_buf.get_int32(); - long key = ser_buf.get_int32(); - long channel = ser_buf.get_int32(); - double time = ser_buf.get_double(); + ser_read_buf.check_input_buffer(24); + long selected = ser_read_buf.get_int32(); + char type = (char) ser_read_buf.get_int32(); + long key = ser_read_buf.get_int32(); + long channel = ser_read_buf.get_int32(); + double time = ser_read_buf.get_double(); if (type == 'n') { - ser_buf.check_input_buffer(20); - float pitch = ser_buf.get_float(); - float loud = ser_buf.get_float(); - double dur = ser_buf.get_double(); + ser_read_buf.check_input_buffer(20); + float pitch = ser_read_buf.get_float(); + float loud = ser_read_buf.get_float(); + double dur = ser_read_buf.get_double(); Alg_note *note = create_note(time, channel, key, pitch, loud, dur); - note->set_selected(selected); - long param_num = ser_buf.get_int32(); + note->set_selected(selected != 0); + long param_num = ser_read_buf.get_int32(); int j; // this builds a list of parameters in the correct order // (although order shouldn't matter) @@ -1541,42 +1636,43 @@ void Alg_track::unserialize_track() } else { assert(type == 'u'); Alg_update *update = create_update(time, channel, key); - update->set_selected(selected); + update->set_selected(selected != 0); unserialize_parameter(&(update->parameter)); append(update); } - ser_buf.get_pad(); + ser_read_buf.get_pad(); } - assert(offset + bytes == ser_buf.get_posn()); + assert(offset + bytes == ser_read_buf.get_posn()); } void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) { - char *attr = ser_buf.get_string(); + Alg_attribute attr = ser_read_buf.get_string(); parm_ptr->attr = symbol_table.insert_string(attr); switch (parm_ptr->attr_type()) { case 'r': - ser_buf.check_input_buffer(8); - parm_ptr->r = ser_buf.get_double(); + ser_read_buf.check_input_buffer(8); + parm_ptr->r = ser_read_buf.get_double(); break; case 's': - parm_ptr->s = heapify(ser_buf.get_string()); + parm_ptr->s = heapify(ser_read_buf.get_string()); break; case 'i': - ser_buf.check_input_buffer(4); - parm_ptr->i = ser_buf.get_int32(); + ser_read_buf.check_input_buffer(4); + parm_ptr->i = ser_read_buf.get_int32(); break; case 'l': - ser_buf.check_input_buffer(4); - parm_ptr->l = (bool) ser_buf.get_int32(); + ser_read_buf.check_input_buffer(4); + parm_ptr->l = ser_read_buf.get_int32() != 0; break; case 'a': - parm_ptr->a = symbol_table.insert_attribute(ser_buf.get_string()); + parm_ptr->a = symbol_table.insert_attribute(ser_read_buf.get_string()); break; } } +//#pragma warning(default: 4800) void Alg_track::set_time_map(Alg_time_map *map) { @@ -1741,9 +1837,10 @@ void Alg_track::paste(double t, Alg_event_list *seq) assert(get_type() == 't'); // seq can be an Alg_event_list, an Alg_track, or an Alg_seq // if it is an Alg_event_list, units_are_seconds must match - bool prev_units_are_seconds = false; + bool prev_units_are_seconds; if (seq->get_type() == 'e') { assert(seq->get_owner()->get_units_are_seconds() == units_are_seconds); + prev_units_are_seconds = seq->get_owner()->get_units_are_seconds(); } else { // make it match Alg_track_ptr tr = (Alg_track_ptr) seq; prev_units_are_seconds = tr->get_units_are_seconds(); @@ -1887,17 +1984,19 @@ Alg_event_list *Alg_track::find(double t, double len, bool all, void Alg_time_sigs::expand() { + assert(maxlen >= len); maxlen = (maxlen + 5); // extra growth for small sizes maxlen += (maxlen >> 2); // add 25% Alg_time_sig_ptr new_time_sigs = new Alg_time_sig[maxlen]; // now do copy memcpy(new_time_sigs, time_sigs, len * sizeof(Alg_time_sig)); - if (time_sigs) delete[] time_sigs; + if (time_sigs) + delete[] time_sigs; time_sigs = new_time_sigs; } -void Alg_time_sigs::insert(double beat, double num, double den) +void Alg_time_sigs::insert(double beat, double num, double den, bool force) { // find insertion point: for (int i = 0; i < len; i++) { @@ -1917,17 +2016,17 @@ void Alg_time_sigs::insert(double beat, double num, double den) // check if redundant with implied initial 4/4 time sig: (i == 0 && num == 4 && den == 4 && within(fmod(beat, 4), 0, ALG_EPS))) { - return; // redundant inserts are ignored here + if (!force) return; // redundant inserts can be ignored here } // make room for new event if (maxlen <= len) expand(); - len++; // insert new event at i memmove(&time_sigs[i + 1], &time_sigs[i], sizeof(Alg_time_sig) * (len - i)); time_sigs[i].beat = beat; time_sigs[i].num = num; time_sigs[i].den = den; + len++; return; } } @@ -1959,55 +2058,225 @@ int Alg_time_sigs::find_beat(double beat) } -void Alg_time_sigs::cut(double start, double end) -{ - // remove time_sig's from start to start+len -- these must be - // in beats (not seconds) - // now rewrite time_sig[]: copy from i_in to i_out (more or less) - int i_in = 0; - int i_out = 0; - // first, figure out where to begin cut region - i_in = find_beat(start); - i_out = i_in; +double Alg_time_sigs::get_bar_len(double beat) +{ + int i = find_beat(beat); + double num = 4.0; + double den = 4.0; + if (i != 0) { + num = time_sigs[i - 1].num; + den = time_sigs[i - 1].den; + } + return 4 * num / den; +} + +void Alg_time_sigs::cut(double start, double end, double dur) +{ + // remove time_sig's from start to end -- these must be + // in beats (not seconds). + // The duration of the whole sequence is dur (beats). + + // If the first bar line after end comes before a time signature + // and does not fall on a bar line, insert a time signature at + // the time of the bar line to retain relative bar line positions + + int i = find_beat(end); + // i is where you would insert a new time sig at beat, + // Case 1: beat coincides with a time sig at i. Time signature + // at beat means that there is a barline at beat, so when beat + // is shifted to start, the relative barline positions are preserved + if (len > 0 && + within(end, time_sigs[i].beat, ALG_EPS)) { + // beat coincides with time signature change, so end is on a barline + /* do nothing */ ; + // Case 2: there is no time signature before end + } else if (i == 0 && (len == 0 || + time_sigs[0].beat > end)) { + // If the next time signature does not fall on a barline, + // then end must not be on a barline, so there is a partial + // measure from end to the next barline. We need + // a time signature there to preserve relative barline + // locations. It may be that the next bar after start is + // due to another time signature, in which case we do not + // need to insert anything. + double measures = end / 4.0; + double imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // start is not on a barline, maybe add one here: + double bar_loc = (int(measures) + 1) * 4.0; + if (bar_loc < dur - ALG_EPS && + (len == 0 || time_sigs[0].beat > bar_loc + ALG_EPS)) { + insert(bar_loc, 4, 4, true); // forced insert + } + } + // This case should never be true because if i == 0, either there + // are no time signatures before beat (Case 2), + // or there is one time signature at beat (Case 1) + } else if (i == 0) { + /* do nothing (might be good to assert(false)) */ ; + // Case 3: i-1 must be the effective time sig position + } else { + // get the time signature in effect at end + Alg_time_sig &tsp = time_sigs[i - 1]; + double beats_per_measure = (tsp.num * 4) / tsp.den; + double measures = (end - tsp.beat) / beats_per_measure; + int imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // end is not on a measure, so we need to insert a time sig + // to force a bar line at the first measure location after + // beat, if any + double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1); + // insert new time signature at bar_loc + // It will have the same time signature, but the position will + // force a barline to match the barline before the shift + // However, we should not insert a barline if there is a + // time signature earlier than the barline time + if (i < len /* time_sigs[i] is the last one */ && + time_sigs[i].beat < bar_loc - ALG_EPS) { + /* do not insert because there's already a time signature */; + } else if (bar_loc < dur - ALG_EPS) { + insert(bar_loc, tsp.num, tsp.den, true); // forced insert + } + } + // else beat coincides with a barline, so no need for an extra + // time signature to force barline alignment + } + + // Figure out if time signature at start matches + // the time signature at end. If not, we need to insert a + // time signature at end to force the correct time signature + // there. + // Find time signature at start: + double start_num = 4.0; // default if no time signature specified + double start_den = 4.0; + i = find_beat(start); + // A time signature at start would go at index i, so the effective + // time signature prior to start is at i - 1. If i == 0, the default + // time signature is in effect prior to start. + if (i != 0) { + start_num = time_sigs[i - 1].num; + start_den = time_sigs[i - 1].den; + } + // Find the time signature at end: + double end_num = 4.0; // default if no time signature specified + double end_den = 4.0; + int j = find_beat(end); + if (j != 0) { + end_num = time_sigs[j - 1].num; + end_den = time_sigs[j - 1].den; + } + // compare: If meter changes and there is no time signature at end, + // insert a time signature at end + if (end < dur - ALG_EPS && + (start_num != end_num || start_den != end_den) && + (j >= len || !within(time_sigs[j].beat, end, ALG_EPS))) { + insert(end, end_num, end_den, true); + } + + // Remove time signatures from start to end (not including one AT + // end, if there is one there. Be careful with ALG_EPS on that one.) + + // since we may have inserted a time signature, find position again: + int i0 = find_beat(start); + int i1 = i0; // scan to end of cut region - while (i_in < len && time_sigs[i_in].beat < end) { - i_in = i_in + 1; - } - // change time_sig at start if necessary - // there's a time_sig that was skipped if i_in > i_out. - // if that's true and the next time change is at end, we're - // ok because it will be copied, but if the next time change - // is after end, then maybe we should insert a time change - // corresponding to what's in effect at end. We can skip this - // insert if it corresponds to whatever is in effect at start - if (i_in > i_out && i_in < len && - time_sigs[i_in].beat > end + ALG_EPS && - (i_out == 0 || time_sigs[i_out - 1].num != time_sigs[i_in - 1].num || - time_sigs[i_out - 1].den != time_sigs[i_in - 1].den)) { - time_sigs[i_out] = time_sigs[i_in - 1]; - time_sigs[i_out].beat = start; + while (i1 < len && time_sigs[i1].beat < end - ALG_EPS) { + i1++; } // scan from end to len(time_sig) - while (i_in < length()) { - Alg_time_sig &ts = time_sigs[i_in]; - ts.beat = ts.beat - (end - start); - time_sigs[i_out] = ts; - i_in = i_in + 1; - i_out = i_out + 1; + while (i1 < len) { + Alg_time_sig &ts = time_sigs[i1]; + ts.beat -= (end - start); + time_sigs[i0] = ts; + i0++; + i1++; } - len = i_out; + len = i1; } void Alg_time_sigs::trim(double start, double end) { - // remove time_sig's not in [start, start+end) + // remove time_sig's not in [start, end), but retain + // barline positions relative to the notes. This means that + // if the meter (time signature) changes between start and + // end that we need to insert a time signature at start. + // Also, if trim() would cause barlines to move, we need to + // insert a time signature on a barline (timesignatures + // imply the beginning of a bar even if the previous bar + // does not have enough beats. Note that bars do not need + // to have an integer number of beats). + // // units must be in beats (not seconds) - // copy from i_in to i_out as we scan time_sig array - int i_in = 0; - int i_out = 0; + // + // Uses Alg_time_sigs::cut() to avoid writing a special case + double dur = end + 1000; + if (len > 0) { + dur = time_sigs[len - 1].beat + 1000; + } + cut(end, dur, dur); + cut(0, start, dur); + +#ifdef IGNORE_THIS_OLD_CODE // first, skip time signatures up to start - i_in = find_beat(start); + int i = find_beat(start); + // i is where you would insert a new time sig at beat, + // Case 1: beat coincides with a time sig at i. Time signature + // at beat means that there is a barline at beat, so when beat + // is shifted to 0, the relative barline positions are preserved + if (len > 0 && + within(start, time_sigs[i].beat, ALG_EPS)) { + // beat coincides with time signature change, so offset must + // be a multiple of beats + /* do nothing */ ; + // Case 2: there is no time signature before start + } else if (i == 0 && (len == 0 || + time_sigs[0].beat > start)) { + // If the next time signature does not fall on a barline, + // then start must not be on a barline, so there is a partial + // measure from start to the next barline. We need + // a time signature there to preserve relative barline + // locations. It may be that the next bar after start is + // due to another time signature, in which case we do not + // need to insert anything. + double measures = start / 4.0; + double imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // start is not on a barline, maybe add one here: + double bar_loc = (int(measures) + 1) * 4.0; + if (len == 0 || time_sigs[1].beat > bar_loc + ALG_EPS) { + insert(bar_loc, 4, 4, true); + } + } + // This case should never be true because if i == 0, either there + // are no time signatures before beat (Case 2), + // or there is one time signature at beat (Case 1) + } else if (i == 0) { + /* do nothing (might be good to assert(false)) */ ; + // Case 3: i-1 must be the effective time sig position + } else { + i -= 1; // index the time signature in effect at start + Alg_time_sig &tsp = time_sigs[i]; + double beats_per_measure = (tsp.num * 4) / tsp.den; + double measures = (start - tsp.beat) / beats_per_measure; + int imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // beat is not on a measure, so we need to insert a time sig + // to force a bar line at the first measure location after + // beat, if any + double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1); + // insert new time signature at bar_loc + // It will have the same time signature, but the position will + // force a barline to match the barline before the shift + insert(bar_loc, tsp.num, tsp.den, true); + } + // else beat coincides with a barline, so no need for an extra + // time signature to force barline alignment + } + // since we may have inserted a time signature, find position again: + int i_in = find_beat(start); + int i_out = 0; + // put time_sig at start if necessary // if 0 < i_in < len, then the time sig at i_in is either // at start or after start. @@ -2034,7 +2303,7 @@ void Alg_time_sigs::trim(double start, double end) time_sigs[0].beat = 0.0; i_out = 1; } - // scan to end of cut region + // copy from i_in to i_out as we scan time_sig array to end of cut region while (i_in < len && time_sigs[i_in].beat < end - ALG_EPS) { Alg_time_sig &ts = time_sigs[i_in]; ts.beat = ts.beat - start; @@ -2043,6 +2312,7 @@ void Alg_time_sigs::trim(double start, double end) i_out++; } len = i_out; +#endif } @@ -2052,7 +2322,7 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) // show(); Alg_time_sigs &from = seq->time_sig; // printf("time_sig::insert from\n"); - from.show(); + // from.show(); // insert time signatures from seq into this time_sigs at start if (len == 0 && from.len == 0) { return; // default applies @@ -2061,6 +2331,13 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) // remember the time signature at the splice point double num_after_splice = 4; double den_after_splice = 4; // default + double num_before_splice = 4; + double den_before_splice = 4; // default + // this is computed for use in aligning beats after the inserted + // time signatures and duration. It is the position of time signature + // in effect immediately after start (the time signature will be + // before start or at start) + double beat_after_splice = 0.0; // three cases: // 1) time sig at splice is at i-1 // for this, we must have len>0 & i>0 @@ -2073,13 +2350,23 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) if (len > 0 && i > 0 && ((i < len && time_sigs[i].beat > start + ALG_EPS) || (i == len))) { + // no time_signature at i num_after_splice = time_sigs[i-1].num; den_after_splice = time_sigs[i-1].den; + beat_after_splice = time_sigs[i - 1].beat; + num_before_splice = num_after_splice; + den_before_splice = den_after_splice; } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) { + // time_signature at i is at "start" beats num_after_splice = time_sigs[i].num; den_after_splice = time_sigs[i].den; + beat_after_splice = start; + if (i > 0) { // time signature before start is at i - 1 + num_before_splice = time_sigs[i-1].num; + den_before_splice = time_sigs[i-1].den; + } } - // i is where insert will go, time_sig[i].beat > start + // i is where insert will go, time_sig[i].beat >= start // begin by adding duration to time_sig's at i and above // move time signatures forward by duration of seq double dur = seq->get_beat_dur(); @@ -2089,46 +2376,189 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) } //printf("time_sig::insert after making space\n"); //show(); - // now insert initial time_signature at start. This may create + // If time signature of "from" is not the effective time signature + // at start, insert a time_signature at start. This may create // an extra measure if seq does not begin on a measure boundary - insert(start, 4, 4); // in case seq uses default starting signature + double num_of_insert = 4.0; + double den_of_insert = 4.0; + double beat_of_insert = 0.0; + /* int first_from_index = 0; // where to start copying from TODO: LMMS commented out unused variable */ + if (from.length() > 0 && from[0].beat < ALG_EPS) { + // there is an initial time signature in "from" + num_of_insert = from[0].num; + den_of_insert = from[0].den; + // since we are handling the first time signature in from, + // we can start copying at index == 1: + /* first_from_index = 1; TODO: LMMS commented out unused variable */ + } + // compare time signatures to see if we need a change at start: + if (num_before_splice != num_of_insert || + den_before_splice != den_of_insert) { + // note that this will overwrite an existing time signature if + // it is within ALG_EPS of start -- this is correct because the + // existing time signature will already be recorded as + // num_after_splice and den_after_splice + insert(start, num_of_insert, den_of_insert); + } //printf("time_sig::insert after 4/4 at start\n"); //show(); // insert time signatures from seq offset by start - for (i = 0; i < from.length(); i++) { - insert(start + from[i].beat, from[i].num, from[i].den); + for (i = 0; i < from.length() && from[i].beat < dur - ALG_EPS; i++) { + num_of_insert = from[i].num; // keep latest time signature info + den_of_insert = from[i].den; + beat_of_insert = from[i].beat; + insert(start + beat_of_insert, num_of_insert, den_of_insert); } //printf("time_sig::insert after pasting in sigs\n"); //show(); - // now insert time signature at end of splice - insert(start + dur, num_after_splice, den_after_splice); + // now insert time signature at end of splice if necessary + // if the time signature changes, we need to insert a time signature + // immediately: + if (num_of_insert != num_after_splice && + den_of_insert != den_after_splice) { + insert(start + dur, num_after_splice, den_after_splice); + num_of_insert = num_after_splice; + den_of_insert = den_after_splice; + beat_of_insert = start + dur; + } + // if the insert had a partial number of measures, we might need an + // additional time signature to realign the barlines after the insert + // To decide, we compare the beat of the first barline on or after + // start before the splice to the beat of the first barline on or + // after start + dur after the splice. In a sense, this is the "same" + // barline, so it should be shifted exactly by dur. + // First, compute the beat of the first barline on or after start: + double beats_per_measure = (num_after_splice * 4) / den_after_splice; + double measures = (start - beat_after_splice) / beats_per_measure; + // Measures might be slightly negative due to rounding. Use max() + // to eliminate any negative rounding error: + int imeasures = int(MAX(measures, 0.0)); + double old_bar_loc = beat_after_splice + (imeasures * beats_per_measure); + if (old_bar_loc < start) old_bar_loc += beats_per_measure; + // now old_bar_loc is the original first bar position after start + // Do similar calculation for position after end after the insertion: + // beats_per_measure already calculated because signatures match + measures = (start + dur - beat_of_insert) / beats_per_measure; + imeasures = int(MAX(measures, 0.0)); + double new_bar_loc = beat_of_insert + (imeasures * beats_per_measure); + if (new_bar_loc < start + dur) new_bar_loc += beats_per_measure; + // old_bar_loc should be shifted by dur: + old_bar_loc += dur; + // now the two bar locations should be equal, but due to rounding, + // they could be off by one measure + double diff = (new_bar_loc - old_bar_loc) + beats_per_measure; + double diff_in_measures = diff / beats_per_measure; + // if diff_in_measures is not (approximately) integer, we need to + // force a barline (time signature) after start + dur to maintain + // the relationship between barliness and notes + if (!within(diff_in_measures, ROUND(diff_in_measures), ALG_EPS)) { + // recall that old_bar_loc is shifted by dur + insert(old_bar_loc, num_after_splice, den_after_splice); + } //printf("time_sig::insert after sig at end of splice\n"); //show(); } -void Alg_time_sigs::insert_beats(double beat, double len) +void Alg_time_sigs::insert_beats(double start, double dur) { - int i; - // find the time_sig entry in effect at t - for (i = 0; i < len; i++) { - if (time_sigs[i].beat < beat + ALG_EPS) { - break; + int i = find_beat(start); + + // time_sigs[i] is after beat and needs to shift + // Compute the time of the first bar at or after beat so that + // a bar can be placed at bar_loc + dur + double tsnum = 4.0; + double tsden = 4.0; + double tsbeat = 0.0; // defaults + + // three cases: + // 1) time sig at splice is at i-1 + // for this, we must have len>0 & i>0 + // two sub-cases: + // A) i < len && time_sig[i].beat > start + // B) i == len + // 2) time_sig at splice is at i + // for this, i < len && time_sig[i].beat ~= start + // 3) time_sig at splice is default 4/4 + if (len > 0 && i > 0 && + ((i < len && time_sigs[i].beat > start + ALG_EPS) || + (i == len))) { + // no time_signature at i + tsnum = time_sigs[i-1].num; + tsden = time_sigs[i-1].den; + tsbeat = time_sigs[i-1].beat; + } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) { + // time_signature at i is at "start" beats + tsnum = time_sigs[i].num; + tsden = time_sigs[i].den; + tsbeat = start; + i++; // we want i to be index of next time signature after start + } + // invariant: i is index of next time signature after start + + // increase beat times from i to len - 1 by dur + for (int j = i; j < len; j++) { + time_sigs[j].beat += dur; + } + + // insert a time signature to maintain bar positions if necessary + double beats_per_measure = (tsnum * 4) / tsden; + double measures = dur / beats_per_measure; // shift distance + int imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // shift is not a whole number of measures, so we may need to insert + // time signature after silence + // compute measures from time signature to next bar after time + measures = (start - tsbeat) / beats_per_measure; + // round up and add to tsbeat to get time of next bar + double bar_loc = tsbeat + beats_per_measure * (int(measures) + 1); + // translate bar_loc by len: + bar_loc += dur; // this is where we want a bar to be, but maybe + // there is a time signature change before bar, in which case we + // should not insert a new time signature + // The next time signature after start is at i if i < len + if (i < len && time_sigs[i].beat < bar_loc) { + /* do not insert */; + } else { + insert(bar_loc, tsnum, tsden); } } - // now, increase beat times by len - for (; i < len; i++) { - time_sigs[i].beat += len; +} + + +double Alg_time_sigs::nearest_beat(double beat) +{ + int i = find_beat(beat); + // i is where we would insert time signature at beat + // case 1: there is no time signature + if (i == 0 && len == 0) { + return ROUND(beat); + // case 2: beat falls approximately on time signature + } else if (i < len && within(time_sigs[i].beat, beat, ALG_EPS)) { + return time_sigs[i].beat; + // case 3: beat is after no time signature and before one + } else if (i == 0) { + double trial_beat = ROUND(beat); + // it is possible that we rounded up past a time signature + if (trial_beat > time_sigs[0].beat - ALG_EPS) { + return time_sigs[0].beat; + } + return trial_beat; } + // case 4: beat is after some time signature + double trial_beat = time_sigs[i - 1].beat + + ROUND(beat - time_sigs[i - 1].beat); + // rounding may advance trial_beat past next time signature: + if (i < len && trial_beat > time_sigs[i].beat - ALG_EPS) { + return time_sigs[i].beat; + } + return trial_beat; } Alg_tracks::~Alg_tracks() { - // Alg_events objects (track data) are not deleted, only the array - if (tracks) { - delete[] tracks; - } + reset(); } @@ -2193,16 +2623,165 @@ void Alg_tracks::reset() // all track events are incorporated into the seq, // so all we need to delete are the arrays of pointers for (int i = 0; i < len; i++) { + // printf("deleting track at %d (%x, this %x) = %x\n", i, &(tracks[i]), + // this, tracks[i]); delete tracks[i]; } if (tracks) delete [] tracks; tracks = NULL; len = 0; - maxlen = 0; // Modified by Ning Hu Nov.19 2002 + maxlen = 0; } -Alg_seq::Alg_seq(const char *filename, bool smf) +void Alg_tracks::set_in_use(bool flag) +{ + for (int i = 0; i < len; i++) { + tracks[i]->in_use = flag; + } +} + + +void Alg_iterator::expand_to(int new_max) +{ + maxlen = new_max; + Alg_pending_event_ptr new_pending_events = new Alg_pending_event[maxlen]; + // now do copy + memcpy(new_pending_events, pending_events, + len * sizeof(Alg_pending_event)); + if (pending_events) { + delete[] pending_events; + } + pending_events = new_pending_events; +} + + +void Alg_iterator::expand() +{ + maxlen = (maxlen + 5); // extra growth for small sizes + maxlen += (maxlen >> 2); // add 25% + expand_to(maxlen); +} + + +Alg_iterator::~Alg_iterator() +{ + if (pending_events) { + delete[] pending_events; + } +} + + +/* in the heap, the children of N are (N+1)*2 and (N+1)*2-1, so + * the parent of N is (N+1)/2-1. This would be easier if arrays + * were 1-based instead of 0-based + */ +#define HEAP_PARENT(loc) ((((loc) + 1) / 2) - 1) +#define FIRST_CHILD(loc) (((loc) * 2) + 1) + +void Alg_iterator::show() +{ + for (int i = 0; i < len; i++) { + Alg_pending_event_ptr p = &(pending_events[i]); + printf(" %d: %p[%ld]@%g on %d\n", i, p->events, p->index, + p->offset, p->note_on); + } +} + + +bool Alg_iterator::earlier(int i, int j) +// see if event i is earlier than event j +{ + // note-offs are scheduled ALG_EPS early so that if a note-off is + // followed immediately with the same timestamp by a note-on (common + // in MIDI files), the note-off will be scheduled first + + double t_i = pending_events[i].time; + double t_j = pending_events[j].time; + + if (t_i < t_j) return true; + // not sure if this case really exists or this is the best rule, but + // we want to give precedence to note-off events + else if (t_i == t_j && pending_events[j].note_on) return true; + return false; +} + + +void Alg_iterator::insert(Alg_events_ptr events, long index, + bool note_on, void *cookie, double offset) +{ + if (len == maxlen) expand(); + pending_events[len].events = events; + pending_events[len].index = index; + pending_events[len].note_on = note_on; + pending_events[len].cookie = cookie; + pending_events[len].offset = offset; + Alg_event_ptr event = (*events)[index]; + pending_events[len].time = (note_on ? event->time : + event->get_end_time() - ALG_EPS) + offset; + /* BEGIN DEBUG * + printf("insert %p=%p[%d] @ %g\n", event, events, index, + pending_events[len].time); + printf(" is_note %d note_on %d time %g dur %g end_time %g offset %g\n", + event->is_note(), note_on, event->time, event->get_duration(), + event->get_end_time(), offset); + } + * END DEBUG */ + int loc = len; + int loc_parent = HEAP_PARENT(loc); + len++; + // sift up: + while (loc > 0 && + earlier(loc, loc_parent)) { + // swap loc with loc_parent + Alg_pending_event temp = pending_events[loc]; + pending_events[loc] = pending_events[loc_parent]; + pending_events[loc_parent] = temp; + loc = loc_parent; + loc_parent = HEAP_PARENT(loc); + } +} + + +bool Alg_iterator::remove_next(Alg_events_ptr &events, long &index, + bool ¬e_on, void *&cookie, + double &offset, double &time) +{ + if (len == 0) return false; // empty! + events = pending_events[0].events; + index = pending_events[0].index; + note_on = pending_events[0].note_on; + offset = pending_events[0].offset; + cookie = pending_events[0].cookie; + offset = pending_events[0].offset; + time = pending_events[0].time; + len--; + pending_events[0] = pending_events[len]; + // sift down + long loc = 0; + long loc_child = FIRST_CHILD(loc); + while (loc_child < len) { + if (loc_child + 1 < len) { + if (earlier(loc_child + 1, loc_child)) { + loc_child++; + } + } + if (earlier(loc_child, loc)) { + Alg_pending_event temp = pending_events[loc]; + pending_events[loc] = pending_events[loc_child]; + pending_events[loc_child] = temp; + loc = loc_child; + loc_child = FIRST_CHILD(loc); + } else { + loc_child = len; + } + } + // printf("After remove:"); show(); + return true; +} + + +Alg_seq::Alg_seq(const char *filename, bool smf, double *offset_ptr) { basic_initialization(); ifstream inf(filename, smf ? ios::binary | ios::in : ios::in); @@ -2212,20 +2791,22 @@ Alg_seq::Alg_seq(const char *filename, bool smf) } if (smf) { error = alg_smf_read(inf, this); + if (offset_ptr) *offset_ptr = 0.0; } else { - error = alg_read(inf, this); + error = alg_read(inf, this, offset_ptr); } inf.close(); } -Alg_seq::Alg_seq(istream &file, bool smf) +Alg_seq::Alg_seq(istream &file, bool smf, double *offset_ptr) { basic_initialization(); if (smf) { error = alg_smf_read(file, this); + if (offset_ptr) *offset_ptr = 0.0; } else { - error = alg_read(file, this); + error = alg_read(file, this, offset_ptr); } } @@ -2284,9 +2865,9 @@ Alg_track_ptr Alg_seq::track(int i) return &(track_list[i]); } +//#pragma warning(disable: 4715) // ok not to return a value here -#pragma GCC diagnostic ignored "-Wreturn-type" // ok not to return a value here -Alg_event_ptr &Alg_seq::operator[](int i) +Alg_event_ptr const &Alg_seq::operator[](int i) { int ntracks = track_list.length(); int tr = 0; @@ -2299,8 +2880,10 @@ Alg_event_ptr &Alg_seq::operator[](int i) } tr++; } - assert(false); // out of bounds + assert(false); // out of bounds + return NULL; } +//#pragma warning(default: 4715) void Alg_seq::convert_to_beats() @@ -2369,11 +2952,12 @@ Alg_seq_ptr Alg_seq::cut(double start, double len, bool all) // return sequence from start to start+len and modify this // sequence by removing that time-span { + double dur = get_dur(); // fix parameters to fall within existing sequence - if (start > get_dur()) return NULL; // nothing to cut + if (start > dur) return NULL; // nothing to cut if (start < 0) start = 0; // can't start before sequence starts - if (start + len > get_dur()) // can't cut after end: - len = get_dur() - start; + if (start + len > dur) // can't cut after end: + len = dur - start; Alg_seq_ptr result = new Alg_seq(); Alg_time_map_ptr map = new Alg_time_map(get_time_map()); @@ -2399,11 +2983,13 @@ Alg_seq_ptr Alg_seq::cut(double start, double len, bool all) // we use len. double ts_start = start; double ts_end = start + len; + double ts_dur = dur; double ts_last_note_off = start + result->last_note_off; if (units_are_seconds) { ts_start = time_map->time_to_beat(ts_start); ts_end = time_map->time_to_beat(ts_end); ts_last_note_off = time_map->time_to_beat(ts_last_note_off); + ts_dur = time_map->time_to_beat(ts_dur); } // result is shifted from start to 0 and has length len, but // time_sig and time_map are copies from this. Adjust time_sig, @@ -2425,9 +3011,9 @@ Alg_seq_ptr Alg_seq::cut(double start, double len, bool all) // we sliced out a portion of each track, so now we need to // slice out the corresponding sections of time_sig and time_map // as well as to adjust the duration. - time_sig.cut(ts_start, ts_end); + time_sig.cut(ts_start, ts_end, ts_dur); time_map->cut(start, len, units_are_seconds); - set_dur(get_dur() - len); + set_dur(dur - len); return result; } @@ -2456,9 +3042,11 @@ void Alg_seq::insert_silence(double t, double len) } else { time_map->insert_beats(t_beats, len_beats); } - if (time_sig.length() > 0) { - time_sig.insert_beats(t_beats, len_beats); - } + time_sig.insert_beats(t_beats, len_beats); + // Final duration is defined to be t + len + whatever was + // in the sequence after t (if any). This translates to + // t + len + max(dur - t, 0) + set_dur(t + len + MAX(get_dur() - t, 0.0)); } @@ -2516,9 +3104,9 @@ Alg_seq *Alg_seq::copy(double start, double len, bool all) void Alg_seq::paste(double start, Alg_seq *seq) { - // insert seq at time; open up space for it - // to manipulate time map, we need units as beats - // save original form so we can convert back if necessary + // Insert seq at time, opening up space for it. + // To manipulate time map, we need units as beats. + // Save original form so we can convert back if necessary. bool units_should_be_seconds = units_are_seconds; bool seq_units_should_be_seconds = seq->get_units_are_seconds(); if (units_are_seconds) { @@ -2595,10 +3183,11 @@ void Alg_seq::clear_track(int track_num, double start, double len, bool all) void Alg_seq::clear(double start, double len, bool all) { // Fix parameters to fall within existing sequence - if (start > get_dur()) return; // nothing to cut + double dur = get_dur(); + if (start > dur) return; // nothing to cut if (start < 0) start = 0; // can't start before sequence starts - if (start + len > get_dur()) // can't cut after end: - len = get_dur() - start; + if (start + len > dur) // can't cut after end: + len = dur - start; for (int i = 0; i < tracks(); i++) clear_track(i, start, len, all); @@ -2606,17 +3195,19 @@ void Alg_seq::clear(double start, double len, bool all) // Put units in beats to match time_sig's. double ts_start = start; double ts_end = start + len; + double ts_dur = dur; if (units_are_seconds) { ts_start = time_map->time_to_beat(ts_start); ts_end = time_map->time_to_beat(ts_end); + ts_dur = time_map->time_to_beat(ts_dur); } // we sliced out a portion of each track, so now we need to // slice out the corresponding sections of time_sig and time_map // as well as to adjust the duration. - time_sig.cut(ts_start, ts_end); + time_sig.cut(ts_start, ts_end, ts_dur); time_map->cut(start, len, units_are_seconds); - set_dur(get_dur() - len); + set_dur(dur - len); } @@ -2674,6 +3265,26 @@ bool Alg_seq::insert_beat(double time, double beat) } +// input is time, return value is time +double Alg_seq::nearest_beat_time(double time, double *beat) +{ + double b = time_map->time_to_beat(time); + b = time_sig.nearest_beat(b); + if (beat) *beat = b; + return time_map->beat_to_time(b); +} + + +bool Alg_seq::stretch_region(double b0, double b1, double dur) +{ + bool units_should_be_seconds = units_are_seconds; + convert_to_beats(); + bool result = time_map->stretch_region(b0, b1, dur); + if (units_should_be_seconds) convert_to_seconds(); + return result; +} + + bool Alg_seq::insert_tempo(double bpm, double beat) { double bps = bpm / 60.0; // convert to beats per second @@ -2724,6 +3335,12 @@ void Alg_seq::add_event(Alg_event_ptr event, int track_num) } +double Alg_seq::get_tempo(double beat) +{ + return time_map->get_tempo(beat); +} + + bool Alg_seq::set_tempo(double bpm, double start_beat, double end_beat) // set tempo from start_beat to end_beat { @@ -2731,12 +3348,21 @@ bool Alg_seq::set_tempo(double bpm, double start_beat, double end_beat) if (start_beat >= end_beat) return false; bool units_should_be_seconds = units_are_seconds; convert_to_beats(); + double dur = get_dur(); bool result = time_map->set_tempo(bpm, start_beat, end_beat); + // preserve sequence duration in beats when tempo changes + set_dur(dur); if (units_should_be_seconds) convert_to_seconds(); return result; } +double Alg_seq::get_bar_len(double beat) +{ + return time_sig.get_bar_len(beat); +} + + void Alg_seq::set_time_sig(double beat, double num, double den) { time_sig.insert(beat, num, den); @@ -2799,43 +3425,61 @@ void Alg_seq::set_events(Alg_event_ptr *events, long len, long max) */ -void Alg_seq::iteration_begin() +void Alg_iterator::begin_seq(Alg_seq_ptr s, void *cookie, double offset) { // keep an array of indexes into tracks - current = new long[track_list.length()]; + // printf("new pending\n"); int i; - for (i = 0; i < track_list.length(); i++) { - current[i] = 0; + for (i = 0; i < s->track_list.length(); i++) { + if (s->track_list[i].length() > 0) { + insert(&(s->track_list[i]), 0, true, cookie, offset); + } } } -Alg_event_ptr Alg_seq::iteration_next() +Alg_event_ptr Alg_iterator::next(bool *note_on, void **cookie_ptr, + double *offset_ptr, double end_time) // return the next event in time from any track { - long cur; // a track index - // find lowest next time of any track: - double next = 1000000.0; - int i, track = 0; - for (i = 0; i < track_list.length(); i++) { - Alg_track &tr = track_list[i]; - cur = current[i]; - if (cur < tr.length() && tr[cur]->time < next) { - next = tr[cur]->time; - track = i; - } - } - if (next < 1000000.0) { - return track_list[track][current[track]++]; - } else { + bool on; + double when; + if (!remove_next(events_ptr, index, on, cookie, offset, when)) { return NULL; } + if (note_on) *note_on = on; + Alg_event_ptr event = (*events_ptr)[index]; + if (on) { + if (note_off_flag && event->is_note() && + (end_time == 0 || + (*events_ptr)[index]->get_end_time() + offset < end_time)) { + // this was a note-on, so insert pending note-off + insert(events_ptr, index, false, cookie, offset); + } + // for both note-ons and updates, insert next event (at index + 1) + // DO NOT INCREMENT index: it must be preserved for request_note_off() + if (index + 1 < events_ptr->length() && + (end_time == 0 || // zero means ignore end time + // stop iterating when end time is reached + (*events_ptr)[index + 1]->time + offset < end_time)) { + insert(events_ptr, index + 1, true, cookie, offset); + } + } + if (cookie_ptr) *cookie_ptr = cookie; + if (offset_ptr) *offset_ptr = offset; + return event; } -void Alg_seq::iteration_end() +void Alg_iterator::request_note_off() +{ + assert(index >= 0 && index < events_ptr->length()); + insert(events_ptr, index, false, cookie, offset); +} + + +void Alg_iterator::end() { - delete[] current; } @@ -2848,17 +3492,25 @@ void Alg_seq::merge_tracks() } // preallocate array for efficiency: Alg_event_ptr *notes = new Alg_event_ptr[sum]; - iteration_begin(); + Alg_iterator iterator(this, false); + iterator.begin(); long notes_index = 0; Alg_event_ptr event; - while (( event = iteration_next() )) { + while ((event = iterator.next())) { notes[notes_index++] = event; } track_list.reset(); // don't need them any more add_track(0); track(0)->set_events(notes, sum, sum); - iteration_end(); + iterator.end(); +} + + +void Alg_seq::set_in_use(bool flag) +{ + Alg_track::set_in_use(flag); + track_list.set_in_use(flag); } diff --git a/plugins/MidiImport/portsmf/allegro.h b/plugins/MidiImport/portsmf/allegro.h index e83d4b46375..ed684fd8a88 100644 --- a/plugins/MidiImport/portsmf/allegro.h +++ b/plugins/MidiImport/portsmf/allegro.h @@ -46,11 +46,11 @@ // Just as serialization uses ser_buf for output, unserialization uses // unser_buf for reading. unser_buf is another static member of Alg_track. -#ifndef __ALLEGRO__ -#define __ALLEGRO__ -#include "debug.h" - -#include "lmmsconfig.h" +#ifndef ALLEGRO_H +#define ALLEGRO_H +#include +#include +#include #define ALG_EPS 0.000001 // epsilon #define ALG_DEFAULT_BPM 100.0 // default tempo @@ -67,7 +67,7 @@ char *heapify(const char *s); // put a string on the heap // the attribute 'tempor' (a real) is stored // as 'rtempor'. To get the string name, just // use attribute+1. -typedef char *Alg_attribute; +typedef const char *Alg_attribute; #define alg_attr_name(a) ((a) + 1) #define alg_attr_type(a) (*(a)) @@ -79,6 +79,19 @@ class Alg_atoms { maxlen = len = 0; atoms = NULL; } + // Note: the code is possibly more correct and faster without the + // following destructor, which will only run after the program takes + // a normal exit. Cleaning up after the program exit slows down the exit, + // and will cause problems if any other destructor tries to reference an + // Alg_atom (which will now be freed). The advantage of this code is + // that Alg_atoms will not be reported as memory leaks by automation + // that doesn't know better. -RBD + virtual ~Alg_atoms() { + for (int i = 0; i < len; i++) { + delete atoms[i]; + } + if (atoms) delete [] atoms; + } // insert/lookup an atttribute Alg_attribute insert_attribute(Alg_attribute attr); // insert/lookup attribute by name (without prefixed type) @@ -86,7 +99,7 @@ class Alg_atoms { private: long maxlen; long len; - char **atoms; + Alg_attribute *atoms; // insert an Attriubute not in table after moving attr to heap Alg_attribute insert_new(const char *name, char attr_type); @@ -101,18 +114,24 @@ extern Alg_atoms symbol_table; // Alg_parameter class typedef class Alg_parameter { public: - ~Alg_parameter(); + // This constructor guarantees that an Alg_parameter can be + // deleted safely without further initialization. It does not + // do anything useful, so it is expected that the creator will + // set attr and store a value in the appropriate union field. Alg_attribute attr; union { double r;// real - char *s; // string + const char *s; // string long i; // integer bool l; // logical - char *a; // symbol (atom) + const char *a; // symbol (atom) }; // anonymous union + + Alg_parameter() { attr = "i"; } + ~Alg_parameter(); void copy(Alg_parameter *); // copy from another parameter - char attr_type() { return alg_attr_type(attr); } - char *attr_name() { return alg_attr_name(attr); } + const char attr_type() { return alg_attr_type(attr); } + const char *attr_name() { return alg_attr_name(attr); } void set_attr(Alg_attribute a) { attr = a; } void show(); } *Alg_parameter_ptr; @@ -139,15 +158,17 @@ typedef class Alg_parameters { // attribute. If you have already done the symbol table lookup/insert // you can do these operations faster (in which case we should add // another set of functions that take attributes as arguments.) - static void insert_real(Alg_parameters **list, char *name, double r); + static void insert_real(Alg_parameters **list, const char *name, double r); // insert string will copy string to heap - static void insert_string(Alg_parameters **list, char *name, char *s); - static void insert_integer(Alg_parameters **list, char *name, long i); - static void insert_logical(Alg_parameters **list, char *name, bool l); - static void insert_atom(Alg_parameters **list, char *name, char *s); + static void insert_string(Alg_parameters **list, const char *name, + const char *s); + static void insert_integer(Alg_parameters **list, const char *name, long i); + static void insert_logical(Alg_parameters **list, const char *name, bool l); + static void insert_atom(Alg_parameters **list, const char *name, + const char *s); static Alg_parameters *remove_key(Alg_parameters **list, const char *name); // find an attribute/value pair - Alg_parameter_ptr find(Alg_attribute *attr); + Alg_parameter_ptr find(Alg_attribute attr); } *Alg_parameters_ptr; @@ -198,11 +219,11 @@ typedef class Alg_event { // attribute (first argument) must agree in type with the second arg. // The last letter of the attribute implies the type (see below). void set_parameter(Alg_parameter_ptr new_parameter); - void set_string_value(char *attr, char *value); - void set_real_value(char *attr, double value); - void set_logical_value(char *attr, bool value); - void set_integer_value(char *attr, long value); - void set_atom_value(char *attr, char *atom); + void set_string_value(const char *attr, const char *value); + void set_real_value(const char *attr, double value); + void set_logical_value(const char *attr, bool value); + void set_integer_value(const char *attr, long value); + void set_atom_value(const char *attr, const char *atom); // Some note methods. These fail (via assert()) if this is not a note: // @@ -222,17 +243,22 @@ typedef class Alg_event { // types. Attribute names end with a type designation: 's', 'r', 'l', // 'i', or 'a'. // - bool has_attribute(char *attr); // test if note has attribute/value pair - char get_attribute_type(char *attr); // get the associated type: + bool has_attribute(const char *attr); // test if note has attribute/value pair + char get_attribute_type(const char *attr); // get the associated type: // 's' = string, // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(char *attr, char *value = NULL); // get the string value - double get_real_value(char *attr, double value = 0.0); // get the real value - bool get_logical_value(char *attr, bool value = false); // get the logical value - long get_integer_value(char *attr, long value = 0); // get the integer value - char *get_atom_value(char *attr, char *value = NULL); // get the atom value - void delete_attribute(char *attr); // delete an attribute/value pair + // get the string value + const char *get_string_value(const char *attr, const char *value = NULL); + // get the real value + double get_real_value(const char *attr, double value = 0.0); + // get the logical value + bool get_logical_value(const char *attr, bool value = false); + // get the integer value + long get_integer_value(const char *attr, long value = 0); + // get the atom value + const char *get_atom_value(const char *attr, const char *value = NULL); + void delete_attribute(const char *attr); // delete an attribute/value pair // (ignore if no matching attribute/value pair exists) // Some attribute/value methods. These fail if this is not an update. @@ -243,13 +269,13 @@ typedef class Alg_event { char get_update_type(); // get the update's type: 's' = string, // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(); // get the update's string value + const char *get_string_value(); // get the update's string value // Notes: Caller does not own the return value. Do not modify. // Do not use after underlying Alg_seq is modified. double get_real_value(); // get the update's real value bool get_logical_value(); // get the update's logical value long get_integer_value(); // get the update's integer value - char *get_atom_value(); // get the update's atom value + const char *get_atom_value(); // get the update's atom value // Notes: Caller does not own the return value. Do not modify. // The return value's lifetime is forever. @@ -305,6 +331,9 @@ typedef class Alg_events { // creating a new track and adding notes to it. It is *not* // updated after uninsert(), so use it with care. double last_note_off; + // initially false, in_use can be used to mark "do not delete". If an + // Alg_events instance is deleted while "in_use", an assertion will fail. + bool in_use; virtual int length() { return len; } Alg_event_ptr &operator[](int i) { assert(i >= 0 && i < len); @@ -314,10 +343,11 @@ typedef class Alg_events { maxlen = len = 0; events = NULL; last_note_off = 0; + in_use = false; } // destructor deletes the events array, but not the // events themselves - ~Alg_events(); + virtual ~Alg_events(); void set_events(Alg_event_ptr *e, long l, long m) { if (events) delete [] events; events = e; len = l; maxlen = m; } @@ -361,7 +391,7 @@ typedef class Alg_event_list : public Alg_events { // When applied to an Alg_seq, events are enumerated track // by track with increasing indices. This operation is not // particularly fast on an Alg_seq. - virtual Alg_event_ptr &operator[](int i); + virtual Alg_event_ptr const &operator[](int i); Alg_event_list() { sequence_number = 0; beat_dur = 0.0; real_dur = 0.0; events_owner = NULL; type = 'e'; } Alg_event_list(Alg_track *owner); @@ -468,8 +498,11 @@ typedef class Alg_time_map { // you want tracks to be in beat units. void insert_beat(double time, double beat); // add a point to the map bool insert_tempo(double tempo, double beat); // insert a tempo change + // get the tempo starting at beat + double get_tempo(double beat); // set the tempo over a region bool set_tempo(double tempo, double start_beat, double end_beat); + bool stretch_region(double b0, double b1, double dur); void cut(double start, double len, bool units_are_seconds); void trim(double start, double end, bool units_are_seconds); void paste(double start, Alg_track *tr); @@ -488,20 +521,76 @@ typedef class Alg_time_map { } *Alg_time_map_ptr; -typedef class Serial_buffer { -private: +// Serial_buffer is an abstract class with common elements of +// Serial_read_buffer and Serial_write_buffer +class Serial_buffer { + protected: char *buffer; char *ptr; long len; -public: + public: Serial_buffer() { buffer = NULL; ptr = NULL; len = 0; } - void init_for_write() { ptr = buffer; } + virtual ~Serial_buffer() { } + long get_posn() { return (long) (ptr - buffer); } long get_len() { return len; } +}; + + +typedef class Serial_read_buffer : public Serial_buffer { +public: + // note that a Serial_read_buffer is initialized for reading by + // setting buffer, but it is not the Serial_read_buffer's responsibility + // to delete the buffer (owner might want to reuse it), so the destructor + // does nothing. + virtual ~Serial_read_buffer() { } +#if defined(_WIN32) +//#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits +//#pragma warning(disable: 4311) // type cast pointer to long warning +#endif + void get_pad() { while ((intptr_t) ptr & 7) ptr++; } +#if defined(_WIN32) +//#pragma warning(default: 4311 546) +#endif + // Prepare to read n bytes from buf. The caller must manage buf: it is + // valid until reading is finished, and it is caller's responsibility + // to free buf when it is no longer needed. + void init_for_read(void *buf, long n) { + buffer = (char *) buf; + ptr = (char *) buf; + len = n; + } + char get_char() { return *ptr++; } + void unget_chars(int n) { ptr -= n; } // undo n get_char() calls + long get_int32() { long i = *((long *) ptr); ptr += 4; return i; } + float get_float() { float f = *((float *) ptr); ptr += 4; return f; } + double get_double() { double d = *((double *) ptr); ptr += sizeof(double); + return d; } + const char *get_string() { char *s = ptr; char *fence = buffer + len; + assert(ptr < fence); (void)fence; // unused variable + while (*ptr++) assert(ptr < fence); + get_pad(); + return s; } + void check_input_buffer(long needed) { + assert(get_posn() + needed <= len); } +} *Serial_read_buffer_ptr; + + +typedef class Serial_write_buffer: public Serial_buffer { + public: + // Note: allegro.cpp declares one static instance of Serial_buffer to + // reduce large memory (re)allocations when serializing tracks for UNDO. + // This destructor will only run when the program exits, which will only + // add overhead to the exit process, but it will eliminate an incorrect + // report of memory leakage from automation that doesn't know better. -RBD + virtual ~Serial_write_buffer() { + if (buffer) delete [] buffer; + } + void init_for_write() { ptr = buffer; } // store_long writes a long at a given offset void store_long(long offset, long value) { assert(offset <= get_posn() - 4); @@ -509,20 +598,33 @@ typedef class Serial_buffer { *loc = value; } void check_buffer(long needed); - void set_string(char *s) { + void set_string(const char *s) { char *fence = buffer + len; - assert(ptr < fence); + assert(ptr < fence); (void)fence; // unused variable + // two brackets surpress a g++ warning, because this is an + // assignment operator inside a test. while ((*ptr++ = *s++)) assert(ptr < fence); - // assert((char *)(((long) (ptr + 7)) & ~7) <= fence); + // 4311 is type cast pointer to long warning + // 4312 is type cast long to pointer warning +#if defined(_WIN32) +//#pragma warning(disable: 4311 4312) +#endif + assert((char *)(((long) (ptr + 7)) & ~7) <= fence); +#if defined(_WIN32) +//#pragma warning(default: 4311 4312) +#endif pad(); } void set_int32(long v) { *((long *) ptr) = v; ptr += 4; } void set_double(double v) { *((double *) ptr) = v; ptr += 8; } void set_float(float v) { *((float *) ptr) = v; ptr += 4; } void set_char(char v) { *ptr++ = v; } -#ifdef LMMS_BUILD_WIN64 - void pad() { while (((long long) ptr) & 7) set_char(0); } -#else - void pad() { while (((long) ptr) & 7) set_char(0); } +#if defined(_WIN32) +//#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits +//#pragma warning(disable: 4311) // type cast pointer to long warning +#endif + void pad() { while ((intptr_t) ptr & 7) set_char(0); } +#if defined(_WIN32) +//#pragma warning(default: 4311 546) #endif void *to_heap(long *len) { *len = get_posn(); @@ -530,29 +632,7 @@ typedef class Serial_buffer { memcpy(newbuf, buffer, *len); return newbuf; } - void init_for_read(void *buf, long n) { - buffer = (char *) buf; - ptr = (char *) buf; - len = n; - } - char get_char() { return *ptr++; } - long get_int32() { long i = *((long *) ptr); ptr += 4; return i; } - float get_float() { float f = *((float *) ptr); ptr += 4; return f; } - double get_double() { double d = *((double *) ptr); ptr += sizeof(double); - return d; } - char *get_string() { char *s = ptr; char *fence = buffer + len; - assert(ptr < fence); - while (*ptr++) assert(ptr < fence); - get_pad(); - return s; } -#ifdef LMMS_BUILD_WIN64 - void get_pad() { while (((long long) ptr) & 7) ptr++; } -#else - void get_pad() { while (((long) ptr) & 7) ptr++; } -#endif - void check_input_buffer(long needed) { - assert(get_posn() + needed <= len); } -} *Serial_buffer_ptr; +} *Serial_write_buffer_ptr; typedef class Alg_seq *Alg_seq_ptr; @@ -564,7 +644,8 @@ typedef class Alg_track : public Alg_event_list { long get_int32(char **p, long *b); double get_double(char **p, long *b); float get_float(char **p, long *b); - static Serial_buffer ser_buf; + static Serial_read_buffer ser_read_buf; + static Serial_write_buffer ser_write_buf; void serialize_parameter(Alg_parameter *parm); // *buffer_ptr points to binary data, bytes_ptr points to how many // bytes have been used so far, len is length of binary data @@ -572,7 +653,7 @@ typedef class Alg_track : public Alg_event_list { public: void serialize_track(); void unserialize_track(); - virtual Alg_event_ptr &operator[](int i) { + virtual Alg_event_ptr const &operator[](int i) { assert(i >= 0 && i < len); return events[i]; } @@ -587,7 +668,9 @@ typedef class Alg_track : public Alg_event_list { // copy constructor: event_list is copied, map is installed and referenced Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map, bool units_are_seconds); - virtual ~Alg_track() { set_time_map(NULL); } + virtual ~Alg_track() { // note: do not call set_time_map(NULL)! + if (time_map) time_map->dereference(); + time_map = NULL; } // Returns a buffer containing a serialization of the // file. It will be an ASCII representation unless text is true. @@ -726,6 +809,7 @@ typedef class Alg_track : public Alg_event_list { virtual Alg_event_list *find(double t, double len, bool all, long channel_mask, long event_type_mask); + virtual void set_in_use(bool flag) { in_use = flag; } // // MIDI playback // @@ -785,11 +869,15 @@ class Alg_time_sigs { void show(); long length() { return len; } int find_beat(double beat); - void insert(double beat, double num, double den); - void cut(double start, double end); // remove from start to end + // get the number of beats per measure starting at beat + double get_bar_len(double beat); + void insert(double beat, double num, double den, bool force = false); + void cut(double start, double end, double dur); // remove from start to end void trim(double start, double end); // retain just start to end void paste(double start, Alg_seq *seq); void insert_beats(double beat, double len); // insert len beats at beat + // find the nearest beat (see Alg_seq::nearest_beat) to beat + double nearest_beat(double beat); }; @@ -816,26 +904,103 @@ typedef class Alg_tracks { void append(Alg_track_ptr track); void add_track(int track_num, Alg_time_map_ptr time_map, bool seconds); void reset(); + void set_in_use(bool flag); // handy to set in_use flag on all tracks } *Alg_tracks_ptr; typedef enum { alg_no_error = 0, // no error reading Allegro or MIDI file alg_error_open = -800, // could not open Allegro or MIDI file - alg_error_syntax // something found in the file that could not be parsed; - // generally you should ignore syntax errors or look at the printed error messages - // because there are some things in standard midi files that we do not handle; - // (maybe we should only set alg_error_syntax when there is a real problem with - // the file as opposed to when there is some warning message for the user) + alg_error_syntax // something found in the file that could not be parsed; + // generally you should ignore syntax errors or look at the printed error + // messages because there are some things in standard midi files that we do + // not handle; (maybe we should only set alg_error_syntax when there is a + // real problem with the file as opposed to when there is some warning + // message for the user) } Alg_error; +typedef struct Alg_pending_event { + void *cookie; // client-provided sequence identifier + Alg_events *events; // the array of events + long index; // offset of this event + bool note_on; // is this a note-on or a note-off (if applicable)? + double offset; // time offset for events + double time; // time for this event +} *Alg_pending_event_ptr; + + +typedef class Alg_iterator { +private: + long maxlen; + void expand(); + void expand_to(int new_max); + long len; + Alg_seq_ptr seq; + Alg_pending_event *pending_events; + // the next four fields are mainly for request_note_off() + Alg_events_ptr events_ptr; // remembers events containing current event + long index; // remembers index of current event + void *cookie; // remembers the cookie associated with next event + double offset; + void show(); + bool earlier(int i, int j); + void insert(Alg_events_ptr events, long index, bool note_on, + void *cookie, double offset); + // returns the info on the next pending event in the priority queue + bool remove_next(Alg_events_ptr &events, long &index, bool ¬e_on, + void *&cookie, double &offset, double &time); +public: + bool note_off_flag; // remembers if we are iterating over note-off + // events as well as note-on and update events + long length() { return len; } + Alg_iterator(Alg_seq_ptr s, bool note_off) { + seq = s; + note_off_flag = note_off; + maxlen = len = 0; + pending_events = NULL; + } + // Normally, iteration is over the events in the one sequence used + // to instatiate the iterator (see above), but with this method, you + // can add more sequences to the iteration. Events are returned in + // time order, so effectively sequence events are merged. + // The optional offset is added to each event time of sequence s + // before merging/sorting. You should call begin_seq() for each + // sequence to be included in the iteration unless you call begin() + // (see below). + void begin_seq(Alg_seq_ptr s, void *cookie = NULL, double offset = 0.0); + ~Alg_iterator(); + // Prepare to enumerate events in order. If note_off_flag is true, then + // iteration_next will merge note-off events into the sequence. If you + // call begin(), you should not normally call begin_seq(). See above. + void begin(void *cookie = NULL) { begin_seq(seq, cookie); } + // return next event (or NULL). If iteration_begin was called with + // note_off_flag = true, and if note_on is not NULL, then *note_on + // is set to true when the result value represents a note-on or update. + // (With note_off_flag, each Alg_note event is returned twice, once + // at the note-on time, with *note_on == true, and once at the note-off + // time, with *note_on == false. If a cookie_ptr is passed, then the + // cookie corresponding to the event is stored at that address + // If end_time is 0, iterate through the entire sequence, but if + // end_time is non_zero, stop iterating at the last event before end_time + Alg_event_ptr next(bool *note_on = NULL, void **cookie_ptr = NULL, + double *offset_ptr = NULL, double end_time = 0); + // Sometimes, the caller wants to receive note-off events for a subset + // of the notes, typically the notes that are played and need to be + // turned off. In this case, when a note is turned on, the client + // should call request_note_off(). This will insert a note-off into + // the queue for the most recent note returned by next(). + void request_note_off(); + void end(); // clean up after enumerating events +} *Alg_iterator_ptr; + + // An Alg_seq is an array of Alg_events, each a sequence of Alg_event, // with a tempo map and a sequence of time signatures // typedef class Alg_seq : public Alg_track { protected: - long *current; // array of indexes used by iteration methods + Alg_iterator_ptr pending; // iterator used internally by Alg_seq methods void serialize_seq(); Alg_error error; // error code set by file readers // an internal function used for writing Allegro track names @@ -860,9 +1025,11 @@ typedef class Alg_seq : public Alg_track { Alg_seq(Alg_track_ref track) { seq_from_track(track); } Alg_seq(Alg_track_ptr track) { seq_from_track(*track); } void seq_from_track(Alg_track_ref tr); - Alg_seq(std::istream &file, bool smf); // create from file - Alg_seq(const char *filename, bool smf); // create from filename - ~Alg_seq(); + // create from file: + Alg_seq(std::istream &file, bool smf, double *offset_ptr = NULL); + // create from filename + Alg_seq(const char *filename, bool smf, double *offset_ptr = NULL); + virtual ~Alg_seq(); int get_read_error() { return error; } void serialize(void **buffer, long *bytes); void copy_time_sigs_to(Alg_seq *dest); // a utility function @@ -874,10 +1041,10 @@ typedef class Alg_seq : public Alg_track { void unserialize_seq(); // write an ascii representation to file - void write(std::ostream &file, bool in_secs); + void write(std::ostream &file, bool in_secs, double offset = 0.0); // returns true on success - bool write(const char *filename); - void smf_write(std::ofstream &file); + bool write(const char *filename, double offset = 0.0); + void smf_write(std::ostream &file); bool smf_write(const char *filename); // Returns the number of tracks @@ -892,7 +1059,7 @@ typedef class Alg_seq : public Alg_track { // caller must not delete the result. Alg_track_ptr track(int); - virtual Alg_event_ptr &operator[](int i); + virtual Alg_event_ptr const &operator[](int i); virtual void convert_to_seconds(); virtual void convert_to_beats(); @@ -917,23 +1084,34 @@ typedef class Alg_seq : public Alg_track { // find index of first score event after time long seek_time(double time, int track_num); bool insert_beat(double time, double beat); + // return the time of the beat nearest to time, also returns beat + // number through beat. This will correspond to an integer number + // of beats from the nearest previous time signature or 0.0, but + // since time signatures need not be on integer beat boundaries + // the beat location may not be on an integer beat (beat locations + // are measured from the beginning which is beat 0. + double nearest_beat_time(double time, double *beat); // warning: insert_tempo may change representation from seconds to beats bool insert_tempo(double bpm, double beat); - + // change the duration from b0 to b1 (beats) to dur (seconds) by + // scaling the intervening tempos + bool stretch_region(double b0, double b1, double dur); // add_event takes a pointer to an event on the heap. The event is not // copied, and this Alg_seq becomes the owner and freer of the event. void add_event(Alg_event_ptr event, int track_num); void add(Alg_event_ptr event) { assert(false); } // call add_event instead - // warning: set_tempo may change representation from seconds to beats + // get the tempo starting at beat + double get_tempo(double beat); bool set_tempo(double bpm, double start_beat, double end_beat); + + // get the bar length in beats starting at beat + double get_bar_len(double beat); void set_time_sig(double beat, double num, double den); void beat_to_measure(double beat, long *measure, double *m_beat, double *num, double *den); // void set_events(Alg_event_ptr *events, long len, long max); void merge_tracks(); // move all track data into one track - void iteration_begin(); // prepare to enumerate events in order - Alg_event_ptr iteration_next(); // return next event (or NULL) - void iteration_end(); // clean up after enumerating events + void set_in_use(bool flag); // set in_use flag on all tracks } *Alg_seq_ptr, &Alg_seq_ref; diff --git a/plugins/MidiImport/portsmf/allegrord.cpp b/plugins/MidiImport/portsmf/allegrord.cpp index 7a1f5beed6f..2dc683afd69 100644 --- a/plugins/MidiImport/portsmf/allegrord.cpp +++ b/plugins/MidiImport/portsmf/allegrord.cpp @@ -1,753 +1,780 @@ -#include "debug.h" -#include "stdlib.h" -#include "string.h" -#include "ctype.h" -#include "trace.h" -#include -#include -#include -#include "strparse.h" -#include "allegro.h" -#include "algrd_internal.h" - -using namespace std; - -#define streql(s1, s2) (strcmp(s1, s2) == 0) -#define field_max 80 - -class Alg_reader { -public: - istream *file; - string input_line; - int line_no; - String_parse line_parser; - bool line_parser_flag; - string field; - bool error_flag; - Alg_seq_ptr seq; - double tsnum; - double tsden; - - Alg_reader(istream *a_file, Alg_seq_ptr new_seq); - void readline(); - Alg_parameters_ptr process_attributes(Alg_parameters_ptr attributes, - double time); - bool parse(); - long parse_chan(string &field); - long parse_int(string &field); - int find_real_in(string &field, int n); - double parse_real(string &field); - void parse_error(string &field, long offset, const char *message); - double parse_dur(string &field, double base); - double parse_after_dur(double dur, string &field, int n, double base); - double parse_loud(string &field); - long parse_key(string &field); - double parse_pitch(string &field); - long parse_after_key(int key, string &field, int n); - long find_int_in(string &field, int n); - bool parse_attribute(string &field, Alg_parameter_ptr parm); - bool parse_val(Alg_parameter_ptr param, string &s, int i); - bool check_type(char type_char, Alg_parameter_ptr param); -}; - - -double Alg_reader::parse_pitch(string &field) -{ - if (isdigit(field[1])) { - int last = find_real_in(field, 1); - string real_string = field.substr(1, last - 1); - return atof(real_string.c_str()); - } else { - return (double) parse_key(field); - } -} - - -// it is the responsibility of the caller to delete -// the seq -Alg_reader::Alg_reader(istream *a_file, Alg_seq_ptr new_seq) -{ - file = a_file; // save the file - line_parser_flag = false; - line_no = 0; - tsnum = 4; // default time signature - tsden = 4; - seq = new_seq; -} - - -Alg_error alg_read(istream &file, Alg_seq_ptr new_seq) - // read a sequence from allegro file -{ - assert(new_seq); - Alg_reader alg_reader(&file, new_seq); - bool err = alg_reader.parse(); - return (err ? alg_error_syntax : alg_no_error); -} - - -void Alg_reader::readline() -{ - // a word about memory management: this Alg_reader has a - // member variable input_line that holds a line of input - // it is reused for each line. input_line is parsed by - // line_parser, which holds a reference to input_line - line_parser_flag = false; - if (getline(*file, input_line)) { - line_parser.init(&input_line); - line_parser_flag = true; - error_flag = false; - } -} - - -Alg_parameters_ptr Alg_reader::process_attributes( - Alg_parameters_ptr attributes, double time) -{ - // print "process_attributes:", attributes - bool ts_flag = false; - if (attributes) { - Alg_parameters_ptr a; - bool in_seconds = seq->get_units_are_seconds(); - if ((a = Alg_parameters::remove_key(&attributes, "tempor"))) { - double tempo = a->parm.r; - seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time)); - } - if ((a = Alg_parameters::remove_key(&attributes, "beatr"))) { - double beat = a->parm.r; - seq->insert_beat(time, beat); - } - if ((a = Alg_parameters::remove_key(&attributes, "timesig_numr"))) { - tsnum = a->parm.r; - ts_flag = true; - } - if ((a = Alg_parameters::remove_key(&attributes, "timesig_denr"))) { - tsden = a->parm.r; - ts_flag = true; - } - if (ts_flag) { - seq->set_time_sig(seq->get_time_map()->time_to_beat(time), - tsnum, tsden); - } - if (in_seconds) seq->convert_to_seconds(); - } - return attributes; // in case it was modified -} - - -bool Alg_reader::parse() -{ - int voice = 0; - int key = 60; - double loud = 100.0; - double pitch = 60.0; - double dur = 1.0; - double time = 0.0; - int track_num = 0; - seq->convert_to_seconds(); - //seq->set_real_dur(0.0); // just in case it's not initialized already - readline(); - bool valid = false; // ignore blank lines - while (line_parser_flag) { - bool time_flag = false; - bool next_flag = false; - double next; - bool voice_flag = false; - bool loud_flag = false; - bool dur_flag = false; - bool new_pitch_flag = false; // "P" syntax or "A"-"G" syntax - double new_pitch = 0.0; - bool new_key_flag = false; // "K" syntax - int new_key = 0; - Alg_parameters_ptr attributes = NULL; - if (line_parser.peek() == '#') { - // look for #track - line_parser.get_nonspace_quoted(field); - if (streql(field.c_str(), "#track")) { - line_parser.get_nonspace_quoted(field); // number - field.insert(0, " "); // need char at beginning because - // parse_int ignores the first character of the argument - track_num = parse_int(field); - seq->add_track(track_num); - } - // maybe we have a sequence or track name - line_parser.get_remainder(field); - // if there is a non-space character after #track n then - // use it as sequence or track name. Note that because we - // skip over spaces, a sequence or track name cannot begin - // with leading blanks. Another decision is that the name - // must be at time zero - if (field.length() > 0) { - // insert the field as sequence name or track name - Alg_update_ptr update = new Alg_update; - update->chan = -1; - update->time = 0; - update->set_identifier(-1); - // sequence name is whatever is on track 0 - // other tracks have track names - const char *attr = - (track_num == 0 ? "seqnames" : "tracknames"); - update->parameter.set_attr(symbol_table.insert_string(attr)); - update->parameter.s = heapify(field.c_str()); - seq->add_event(update, track_num); - } - } else { - // we must have a track to insert into - if (seq->tracks() == 0) seq->add_track(0); - line_parser.get_nonspace_quoted(field); - char pk = line_parser.peek(); - // attributes are parsed as two adjacent nonspace_quoted tokens - // so we have to conditionally call get_nonspace_quoted() again - if (pk && !isspace(pk)) { - string field2; - line_parser.get_nonspace_quoted(field2); - field.append(field2); - } - while (field[0]) { - char first = toupper(field[0]); - if (strchr("ABCDEFGKLPUSIQHW-", first)) { - valid = true; // it's a note or event - } - if (first == 'V') { - if (voice_flag) { - parse_error(field, 0, "Voice specified twice"); - } else { - voice = parse_chan(field); - } - voice_flag = true; - } else if (first == 'T') { - if (time_flag) { - parse_error(field, 0, "Time specified twice"); - } else { - time = parse_dur(field, 0.0); - } - time_flag = true; - } else if (first == 'N') { - if (next_flag) { - parse_error(field, 0, "Next specified twice"); - } else { - next = parse_dur(field, time); - } - next_flag = true; - } else if (first == 'K') { - if (new_key_flag) { - parse_error(field, 0, "Key specified twice"); - } else { - new_key = parse_key(field); - new_key_flag = true; - } - } else if (first == 'L') { - if (loud_flag) { - parse_error(field, 0, "Loudness specified twice"); - } else { - loud = parse_loud(field); - } - loud_flag = true; - } else if (first == 'P') { - if (new_pitch_flag) { - parse_error(field, 0, "Pitch specified twice"); - } else { - new_pitch = parse_pitch(field); - new_pitch_flag = true; - } - } else if (first == 'U') { - if (dur_flag) { - parse_error(field, 0, "Dur specified twice"); - } else { - dur = parse_dur(field, time); - dur_flag = true; - } - } else if (strchr("SIQHW", first)) { - if (dur_flag) { - parse_error(field, 0, "Dur specified twice"); - } else { - // prepend 'U' to field, copy EOS too - field.insert(0, 1, 'U'); - dur = parse_dur(field, time); - dur_flag = true; - } - } else if (strchr("ABCDEFG", first)) { - if (new_pitch_flag) { - parse_error(field, 0, "Pitch specified twice"); - } else { - // prepend 'P' to field - field.insert(0, 1, 'P'); - new_pitch = parse_pitch(field); - new_pitch_flag = true; - } - } else if (first == '-') { - Alg_parameter parm; - if (parse_attribute(field, &parm)) { // enter attribute-value pair - attributes = new Alg_parameters(attributes); - attributes->parm = parm; - parm.s = NULL; // protect string from deletion by destructor - } - } else { - parse_error(field, 0, "Unknown field"); - } - - if (error_flag) { - field[0] = 0; // exit the loop - } else { - line_parser.get_nonspace_quoted(field); - pk = line_parser.peek(); - // attributes are parsed as two adjacent nonspace_quoted - // tokens so we have to conditionally call - // get_nonspace_quoted() again - if (pk && !isspace(pk)) { - string field2; - line_parser.get_nonspace_quoted(field2); - field.append(field2); - } - } - } - // a case analysis: - // Key < 128 implies pitch unless pitch is explicitly given - // Pitch implies Key unless key is explicitly given, - // Pitch is rounded to nearest integer to determine the Key - // if necessary, so MIDI files will lose the pitch fraction - // A-G is a Pitch specification (therefore it implies Key) - // K60 P60 -- both are specified, use 'em - // K60 P60 C4 -- overconstrained, an error - // K60 C4 -- OK, but K60 is already implied by C4 - // K60 -- OK, pitch is 60 - // C4 P60 -- over constrained - // P60 -- OK, key is 60 - // P60.1 -- OK, key is 60 - // C4 -- OK, key is 60, pitch is 60 - // -- OK, key and pitch from before - // K200 P60 -- ok, pitch is 60 - // K200 with neither P60 nor C4 uses - // pitch from before - - // figure out what the key/instance is: - if (new_key_flag) { // it was directly specified - key = new_key; - } else if (new_pitch_flag) { - // pitch was specified, but key was not; get key from pitch - key = (int) (new_pitch + 0.5); // round to integer key number - } - if (new_pitch_flag) { - pitch = new_pitch; - } else if (key < 128 && new_key_flag) { - // no explicit pitch, but key < 128, so it implies pitch - pitch = key; - new_pitch_flag = true; - } - // now we've acquired new parameters - // if (it is a note, then enter the note - if (valid) { - // change tempo or beat - attributes = process_attributes(attributes, time); - // if there's a duration or pitch, make a note: - if (new_pitch_flag || dur_flag) { - Alg_note_ptr note_ptr = new Alg_note; - note_ptr->chan = voice; - note_ptr->time = time; - note_ptr->dur = dur; - note_ptr->set_identifier(key); - note_ptr->pitch = pitch; - note_ptr->loud = loud; - note_ptr->parameters = attributes; - seq->add_event(note_ptr, track_num); // sort later - if (seq->get_real_dur() < (time + dur)) seq->set_real_dur(time + dur); - } else { - int update_key = -1; - // key must appear explicitly; otherwise - // update applies to channel - if (new_key_flag) { - update_key = key; - } - if (loud_flag) { - Alg_update_ptr new_upd = new Alg_update; - new_upd->chan = voice; - new_upd->time = time; - new_upd->set_identifier(update_key); - new_upd->parameter.set_attr(symbol_table.insert_string("loudr")); - new_upd->parameter.r = pitch; - seq->add_event(new_upd, track_num); - if (seq->get_real_dur() < time) seq->set_real_dur(time); - } - if (attributes) { - while (attributes) { - Alg_update_ptr new_upd = new Alg_update; - new_upd->chan = voice; - new_upd->time = time; - new_upd->set_identifier(update_key); - new_upd->parameter = attributes->parm; - seq->add_event(new_upd, track_num); - Alg_parameters_ptr p = attributes; - attributes = attributes->next; - p->parm.s = NULL; // so we don't delete the string - delete p; - } - } - } - if (next_flag) { - time = time + next; - } else if (dur_flag || new_pitch_flag) { // a note: incr by dur - time = time + dur; - } - } - } - readline(); - } - if (!error_flag) { // why not convert even if there was an error? -RBD - seq->convert_to_seconds(); // make sure format is correct - } - // real_dur is valid, translate to beat_dur - seq->set_beat_dur((seq->get_time_map())->time_to_beat(seq->get_real_dur())); - return error_flag; -} - - -long Alg_reader::parse_chan(string &field) -{ - const char *int_string = field.c_str() + 1; - const char *msg = "Integer or - expected"; - const char *p = int_string; - char c; - // check that all chars in int_string are digits or '-': - while ((c = *p++)) { - if (!isdigit(c) && c != '-') { - parse_error(field, p - field.c_str() - 1, msg); - return 0; - } - } - p--; // p now points to end-of-string character - if (p - int_string == 0) { - // bad: string length is zero - parse_error(field, 1, msg); - return 0; - } - if (p - int_string == 1 && int_string[0] == '-') { - // special case: entire string is "-", interpret as -1 - return -1; - } - return atoi(int_string); -} - - -long Alg_reader::parse_int(string &field) -{ - const char *int_string = field.c_str() + 1; - const char *msg = "Integer expected"; - const char *p = int_string; - char c; - // check that all chars in int_string are digits: - while ((c = *p++)) { - if (!isdigit(c)) { - parse_error(field, p - field.c_str() - 1, msg); - return 0; - } - } - p--; // p now points to end-of-string character - if (p - int_string == 0) { - // bad: string length is zero - parse_error(field, 1, msg); - return 0; - } - return atoi(int_string); -} - - -int Alg_reader::find_real_in(string &field, int n) -{ - // scans from offset n to the end of a real constant - bool decimal = false; - int len = field.length(); - for (int i = n; i < len; i++) { - char c = field[i]; - if (!isdigit(c)) { - if (c == '.' && !decimal) { - decimal = true; - } else { - return i; - } - } - } - return field.length(); -} - - -double Alg_reader::parse_real(string &field) -{ - const char *msg = "Real expected"; - int last = find_real_in(field, 1); - string real_string = field.substr(1, last - 1); - if (last <= 1 || last < (int) field.length()) { - parse_error(field, 1, msg); - return 0; - } - return atof(real_string.c_str()); -} - - -void Alg_reader::parse_error(string &field, long offset, const char *message) -{ - int position = line_parser.pos - field.length() + offset; - error_flag = true; - puts(line_parser.str->c_str()); - for (int i = 0; i < position; i++) { - putc(' ', stdout); - } - putc('^', stdout); - printf(" %s\n", message); -} - - -double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; - - -double Alg_reader::parse_dur(string &field, double base) -{ - const char *msg = "Duration expected"; - const char *durs = "SIQHW"; - const char *p; - int last; - double dur; - if (field.length() < 2) { - // fall through to error message - return -1; - } else if (isdigit(field[1])) { - last = find_real_in(field, 1); - string real_string = field.substr(1, last - 1); - dur = atof(real_string.c_str()); - // convert dur from seconds to beats - dur = seq->get_time_map()->time_to_beat(base + dur) - - seq->get_time_map()->time_to_beat(base); - } else if ((p = strchr(durs, toupper(field[1])))) { - dur = duration_lookup[p - durs]; - last = 2; - } else { - parse_error(field, 1, msg); - return 0; - } - dur = parse_after_dur(dur, field, last, base); - dur = seq->get_time_map()->beat_to_time( - seq->get_time_map()->time_to_beat(base) + dur) - base; - return dur; -} - - -double Alg_reader::parse_after_dur(double dur, string &field, - int n, double base) -{ - if ((int) field.length() == n) { - return dur; - } - if (toupper(field[n]) == 'T') { - return parse_after_dur(dur * 2/3, field, n + 1, base); - } - if (field[n] == '.') { - return parse_after_dur(dur * 1.5, field, n + 1, base); - } - if (isdigit(field[n])) { - int last = find_real_in(field, n); - string a_string = field.substr(n, last - n); - double f = atof(a_string.c_str()); - return parse_after_dur(dur * f, field, last, base); - } - if (field[n] == '+') { - string a_string = field.substr(n + 1); - return dur + parse_dur( - a_string, seq->get_time_map()->beat_to_time( - seq->get_time_map()->time_to_beat(base) + dur)); - } - parse_error(field, n, "Unexpected character in duration"); - return dur; -} - -struct loud_lookup_struct { - const char *str; - int val; -} loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100}, - {"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60}, - {NULL, 0} }; - - -double Alg_reader::parse_loud(string &field) -{ - const char *msg = "Loudness expected"; - if (isdigit(field[1])) { - return parse_int(field); - } else { - string dyn = field.substr(1); - transform(dyn.begin(), dyn.end(), dyn.begin(), ::toupper); - for (int i = 0; loud_lookup[i].str; i++) { - if (streql(loud_lookup[i].str, dyn.c_str())) { - return (double) loud_lookup[i].val; - } - } - } - parse_error(field, 1, msg); - return 100.0; -} - - -int key_lookup[] = {21, 23, 12, 14, 16, 17, 19}; - - -// the field can be K or K[A-G] or P[A-G] -// (this can be called from parse_pitch() to handle [A-G]) -// Notice that the routine ignores the first character: K or P -// -long Alg_reader::parse_key(string &field) -{ - const char *msg = "Pitch expected"; - const char *pitches = "ABCDEFG"; - const char *p; - if (isdigit(field[1])) { - // This routine would not have been called if field = "P" - // so it must be "K" so must be an integer. - return parse_int(field); - } else if ((p = strchr(pitches, toupper(field[1])))) { - long key = key_lookup[p - pitches]; - key = parse_after_key(key, field, 2); - return key; - } - parse_error(field, 1, msg); - return 0; -} - - -long Alg_reader::parse_after_key(int key, string &field, int n) -{ - if ((int) field.length() == n) { - return key; - } - char c = toupper(field[n]); - if (c == 'S') { - return parse_after_key(key + 1, field, n + 1); - } - if (c == 'F') { - return parse_after_key(key - 1, field, n + 1); - } - if (isdigit(field[n])) { - int last = find_int_in(field, n); - string octave = field.substr(n, last - n); - int oct = atoi(octave.c_str()); - return parse_after_key(key + oct * 12, field, last); - } - parse_error(field, n, "Unexpected character in pitch"); - return key; -} - - -long Alg_reader::find_int_in(string &field, int n) -{ - while ((int) field.length() > n && isdigit(field[n])) { - n = n + 1; - } - return n; -} - - -bool Alg_reader::parse_attribute(string &field, Alg_parameter_ptr param) -{ - int i = 1; - while (i < (int) field.length()) { - if (field[i] == ':') { - string attr = field.substr(1, i - 1); - char type_char = field[i - 1]; - if (strchr("iarsl", type_char)) { - param->set_attr(symbol_table.insert_string(attr.c_str())); - parse_val(param, field, i + 1); - } else { - parse_error(field, 0, "attribute needs to end with typecode: i,a,r,s, or l"); - } - return !error_flag; - } - i = i + 1; - } - return false; -} - - -bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i) -{ - int len = (int) s.length(); - if (i >= len) { - return false; - } - if (s[i] == '"') { - if (!check_type('s', param)) { - return false; - } - // note: (len - i) includes 2 quote characters but no EOS character - // so total memory to allocate is (len - i) - 1 - char *r = new char[(len - i) - 1]; - strncpy(r, s.c_str() + i + 1, (len - i) - 2); - r[(len - i) - 2] = 0; // terminate the string - param->s = r; - } else if (s[i] == '\'') { - if (!check_type('a', param)) { - return false; - } - string r = s.substr(i + 1, len - i - 2); - param->a = symbol_table.insert_string(r.c_str()); - } else if (param->attr_type() == 'l') { - if (streql(s.c_str() + i, "true") || - streql(s.c_str() + i, "t")) { - param->l = true; - } else if (streql(s.c_str() + i, "false") || - streql(s.c_str() + i, "nil")) { - param->l = false; - } else return false; - } else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') { - int pos = i; - bool period = false; - if (s[pos] == '-') { - pos++; - } - while (pos < len) { - if (isdigit(s[pos])) { - ; - } else if (!period && s[pos] == '.') { - period = true; - } else { - parse_error(s, pos, "Unexpected char in number"); - return false; - } - pos = pos + 1; - } - string r = s.substr(i, len - i); - if (period) { - if (!check_type('r', param)) { - return false; - } - param->r = atof(r.c_str()); - } else { - if (param->attr_type() == 'r') { - param->r = atoi(r.c_str()); - } else if (!check_type('i', param)) { - return false; - } else { - param->i = atoi(r.c_str()); - } - } - } else { - parse_error(s, i, "invalid value"); - return false; - } - return true; -} - - -bool Alg_reader::check_type(char type_char, Alg_parameter_ptr param) -{ - return param->attr_type() == type_char; -} - - -//duration_lookup = {"S": 0.5, "I": 0.5, "Q": 1, "H": 2, "W": 4} -//key_lookup = {"C": 12, "D": 14, "E": 16, "F": 17, "G": 19, "A": 21, "B": 23} - -/* -def test(): - reader = Alg_reader(open("data\\test.gro", "r")) - reader.parse() - score = reader->seq.notes - print "score:", score - reader = nil -*/ +#include "assert.h" +#include "stdlib.h" +#include "string.h" +#include "ctype.h" +#include "trace.h" +#include +#include +#include +#include "strparse.h" +#include "allegro.h" +#include "algrd_internal.h" + +using namespace std; + +#define streql(s1, s2) (strcmp(s1, s2) == 0) +#define field_max 80 + +class Alg_reader { +public: + istream *file; + string input_line; + int line_no; + String_parse line_parser; + bool line_parser_flag; + string field; + bool error_flag; + Alg_seq_ptr seq; + double tsnum; + double tsden; + double offset; + bool offset_found; + + Alg_reader(istream *a_file, Alg_seq_ptr new_seq); + void readline(); + Alg_parameters_ptr process_attributes(Alg_parameters_ptr attributes, + double time); + bool parse(); + long parse_chan(string &field); + long parse_int(string &field); + int find_real_in(string &field, int n); + double parse_real(string &field); + void parse_error(string &field, long offset, char *message); + void parse_error(string &field, long offset, const char *message); + double parse_dur(string &field, double base); + double parse_after_dur(double dur, string &field, int n, double base); + double parse_loud(string &field); + long parse_key(string &field); + double parse_pitch(string &field); + long parse_after_key(int key, string &field, int n); + long find_int_in(string &field, int n); + bool parse_attribute(string &field, Alg_parameter_ptr parm); + bool parse_val(Alg_parameter_ptr param, string &s, int i); + bool check_type(char type_char, Alg_parameter_ptr param); +}; + + +double Alg_reader::parse_pitch(string &field) +{ + if (isdigit(field[1])) { + int last = find_real_in(field, 1); + string real_string = field.substr(1, last - 1); + return atof(real_string.c_str()); + } else { + return (double) parse_key(field); + } +} + + +// it is the responsibility of the caller to delete +// the seq +Alg_reader::Alg_reader(istream *a_file, Alg_seq_ptr new_seq) +{ + file = a_file; // save the file + line_parser_flag = false; + line_no = 0; + tsnum = 4; // default time signature + tsden = 4; + seq = new_seq; + offset = 0.0; + offset_found = false; +} + + +Alg_error alg_read(istream &file, Alg_seq_ptr new_seq, double *offset_ptr) + // read a sequence from allegro file +{ + assert(new_seq); + Alg_reader alg_reader(&file, new_seq); + bool err = alg_reader.parse(); + if (!err && offset_ptr) { + *offset_ptr = alg_reader.offset; + } + return (err ? alg_error_syntax : alg_no_error); +} + + +void Alg_reader::readline() +{ + // a word about memory management: this Alg_reader has a + // member variable input_line that holds a line of input + // it is reused for each line. input_line is parsed by + // line_parser, which holds a reference to input_line + line_parser_flag = false; + if (getline(*file, input_line)) { + line_parser.init(&input_line); + line_parser_flag = true; + error_flag = false; + } +} + + +Alg_parameters_ptr Alg_reader::process_attributes( + Alg_parameters_ptr attributes, double time) +{ + // print "process_attributes:", attributes + bool ts_flag = false; + if (attributes) { + Alg_parameters_ptr a; + bool in_seconds = seq->get_units_are_seconds(); + if ((a = Alg_parameters::remove_key(&attributes, "tempor"))) { + double tempo = a->parm.r; + seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time)); + } + if ((a = Alg_parameters::remove_key(&attributes, "beatr"))) { + double beat = a->parm.r; + seq->insert_beat(time, beat); + } + if ((a = Alg_parameters::remove_key(&attributes, "timesig_numr"))) { + tsnum = a->parm.r; + ts_flag = true; + } + if ((a = Alg_parameters::remove_key(&attributes, "timesig_denr"))) { + tsden = a->parm.r; + ts_flag = true; + } + if (ts_flag) { + seq->set_time_sig(seq->get_time_map()->time_to_beat(time), + tsnum, tsden); + } + if (in_seconds) seq->convert_to_seconds(); + } + return attributes; // in case it was modified +} + + +bool Alg_reader::parse() +{ + int voice = 0; + int key = 60; + double loud = 100.0; + double pitch = 60.0; + double dur = 1.0; + double time = 0.0; + int track_num = 0; + seq->convert_to_seconds(); + //seq->set_real_dur(0.0); // just in case it's not initialized already + readline(); + bool valid = false; // ignore blank lines + while (line_parser_flag) { + bool time_flag = false; + bool next_flag = false; + double next = 0; + bool voice_flag = false; + bool loud_flag = false; + bool dur_flag = false; + bool new_pitch_flag = false; // "P" syntax or "A"-"G" syntax + double new_pitch = 0.0; + bool new_key_flag = false; // "K" syntax + int new_key = 0; + Alg_parameters_ptr attributes = NULL; + if (line_parser.peek() == '#') { + // look for #track + line_parser.get_nonspace_quoted(field); + if (streql(field.c_str(), "#track")) { + line_parser.get_nonspace_quoted(field); // number + field.insert(0, " "); // need char at beginning because + // parse_int ignores the first character of the argument + track_num = parse_int(field); + seq->add_track(track_num); + + // maybe we have a sequence or track name + line_parser.get_remainder(field); + // if there is a non-space character after #track n then + // use it as sequence or track name. Note that because we + // skip over spaces, a sequence or track name cannot begin + // with leading blanks. Another decision is that the name + // must be at time zero + if (field.length() > 0) { + // insert the field as sequence name or track name + Alg_update_ptr update = new Alg_update; + update->chan = -1; + update->time = 0; + update->set_identifier(-1); + // sequence name is whatever is on track 0 + // other tracks have track names + const char *attr = + (track_num == 0 ? "seqnames" : "tracknames"); + update->parameter.set_attr( + symbol_table.insert_string(attr)); + update->parameter.s = heapify(field.c_str()); + seq->add_event(update, track_num); + } + } else if (streql(field.c_str(), "#offset")) { + if (offset_found) { + parse_error(field, 0, "#offset specified twice"); + } + offset_found = true; + line_parser.get_nonspace_quoted(field); // number + field.insert(0, " "); // need char at beginning because + // parse_real ignores first character in the argument + offset = parse_real(field); + } + } else { + // we must have a track to insert into + if (seq->tracks() == 0) seq->add_track(0); + line_parser.get_nonspace_quoted(field); + char pk = line_parser.peek(); + // attributes are parsed as two adjacent nonspace_quoted tokens + // so we have to conditionally call get_nonspace_quoted() again + if (pk && !isspace(pk)) { + string field2; + line_parser.get_nonspace_quoted(field2); + field.append(field2); + } + while (field[0]) { + char first = toupper(field[0]); + if (strchr("ABCDEFGKLPUSIQHW-", first)) { + valid = true; // it's a note or event + } + if (first == 'V') { + if (voice_flag) { + parse_error(field, 0, "Voice specified twice"); + } else { + voice = parse_chan(field); + } + voice_flag = true; + } else if (first == 'T') { + if (time_flag) { + parse_error(field, 0, "Time specified twice"); + } else { + time = parse_dur(field, 0.0); + } + time_flag = true; + } else if (first == 'N') { + if (next_flag) { + parse_error(field, 0, "Next specified twice"); + } else { + next = parse_dur(field, time); + } + next_flag = true; + } else if (first == 'K') { + if (new_key_flag) { + parse_error(field, 0, "Key specified twice"); + } else { + new_key = parse_key(field); + new_key_flag = true; + } + } else if (first == 'L') { + if (loud_flag) { + parse_error(field, 0, "Loudness specified twice"); + } else { + loud = parse_loud(field); + } + loud_flag = true; + } else if (first == 'P') { + if (new_pitch_flag) { + parse_error(field, 0, "Pitch specified twice"); + } else { + new_pitch = parse_pitch(field); + new_pitch_flag = true; + } + } else if (first == 'U') { + if (dur_flag) { + parse_error(field, 0, "Dur specified twice"); + } else { + dur = parse_dur(field, time); + dur_flag = true; + } + } else if (strchr("SIQHW", first)) { + if (dur_flag) { + parse_error(field, 0, "Dur specified twice"); + } else { + // prepend 'U' to field, copy EOS too + field.insert((unsigned int) 0, 1, 'U'); + dur = parse_dur(field, time); + dur_flag = true; + } + } else if (strchr("ABCDEFG", first)) { + if (new_pitch_flag) { + parse_error(field, 0, "Pitch specified twice"); + } else { + // prepend 'P' to field + field.insert((unsigned int) 0, 1, 'P'); + new_pitch = parse_pitch(field); + new_pitch_flag = true; + } + } else if (first == '-') { + Alg_parameter parm; + if (parse_attribute(field, &parm)) { // enter attribute-value pair + attributes = new Alg_parameters(attributes); + attributes->parm = parm; + parm.s = NULL; // protect string from deletion by destructor + } + } else { + parse_error(field, 0, "Unknown field"); + } + + if (error_flag) { + field[0] = 0; // exit the loop + } else { + line_parser.get_nonspace_quoted(field); + pk = line_parser.peek(); + // attributes are parsed as two adjacent nonspace_quoted + // tokens so we have to conditionally call + // get_nonspace_quoted() again + if (pk && !isspace(pk)) { + string field2; + line_parser.get_nonspace_quoted(field2); + field.append(field2); + } + } + } + // a case analysis: + // Key < 128 implies pitch unless pitch is explicitly given + // Pitch implies Key unless key is explicitly given, + // Pitch is rounded to nearest integer to determine the Key + // if necessary, so MIDI files will lose the pitch fraction + // A-G is a Pitch specification (therefore it implies Key) + // K60 P60 -- both are specified, use 'em + // K60 P60 C4 -- overconstrained, an error + // K60 C4 -- OK, but K60 is already implied by C4 + // K60 -- OK, pitch is 60 + // C4 P60 -- over constrained + // P60 -- OK, key is 60 + // P60.1 -- OK, key is 60 + // C4 -- OK, key is 60, pitch is 60 + // -- OK, key and pitch from before + // K200 P60 -- ok, pitch is 60 + // K200 with neither P60 nor C4 uses + // pitch from before + + // figure out what the key/instance is: + if (new_key_flag) { // it was directly specified + key = new_key; + } else if (new_pitch_flag) { + // pitch was specified, but key was not; get key from pitch + key = (int) (new_pitch + 0.5); // round to integer key number + } + if (new_pitch_flag) { + pitch = new_pitch; + } else if (key < 128 && new_key_flag) { + // no explicit pitch, but key < 128, so it implies pitch + pitch = key; + new_pitch_flag = true; + } + // now we've acquired new parameters + // if (it is a note, then enter the note + if (valid) { + // change tempo or beat + attributes = process_attributes(attributes, time); + // if there's a duration or pitch, make a note: + if (new_pitch_flag || dur_flag) { + Alg_note_ptr note_ptr = new Alg_note; + note_ptr->chan = voice; + note_ptr->time = time; + note_ptr->dur = dur; + note_ptr->set_identifier(key); + note_ptr->pitch = (float) pitch; + note_ptr->loud = (float) loud; + note_ptr->parameters = attributes; + seq->add_event(note_ptr, track_num); // sort later + if (seq->get_real_dur() < (time + dur)) seq->set_real_dur(time + dur); + } else { + int update_key = -1; + // key must appear explicitly; otherwise + // update applies to channel + if (new_key_flag) { + update_key = key; + } + if (loud_flag) { + Alg_update_ptr new_upd = new Alg_update; + new_upd->chan = voice; + new_upd->time = time; + new_upd->set_identifier(update_key); + new_upd->parameter.set_attr(symbol_table.insert_string("loudr")); + new_upd->parameter.r = pitch; + seq->add_event(new_upd, track_num); + if (seq->get_real_dur() < time) seq->set_real_dur(time); + } + if (attributes) { + while (attributes) { + Alg_update_ptr new_upd = new Alg_update; + new_upd->chan = voice; + new_upd->time = time; + new_upd->set_identifier(update_key); + new_upd->parameter = attributes->parm; + seq->add_event(new_upd, track_num); + Alg_parameters_ptr p = attributes; + attributes = attributes->next; + p->parm.s = NULL; // so we don't delete the string + delete p; + } + } + } + if (next_flag) { + time = time + next; + } else if (dur_flag || new_pitch_flag) { // a note: incr by dur + time = time + dur; + } + } + } + readline(); + } + if (!error_flag) { // why not convert even if there was an error? -RBD + seq->convert_to_seconds(); // make sure format is correct + } + // real_dur is valid, translate to beat_dur + seq->set_beat_dur((seq->get_time_map())->time_to_beat(seq->get_real_dur())); + return error_flag; +} + + +long Alg_reader::parse_chan(string &field) +{ + const char *int_string = field.c_str() + 1; + const char *msg = "Integer or - expected"; + const char *p = int_string; + char c; + // check that all chars in int_string are digits or '-': + while ((c = *p++)) { + if (!isdigit(c) && c != '-') { + parse_error(field, p - field.c_str() - 1, msg); + return 0; + } + } + p--; // p now points to end-of-string character + if (p - int_string == 0) { + // bad: string length is zero + parse_error(field, 1, msg); + return 0; + } + if (p - int_string == 1 && int_string[0] == '-') { + // special case: entire string is "-", interpret as -1 + return -1; + } + return atoi(int_string); +} + + +long Alg_reader::parse_int(string &field) +{ + const char *int_string = field.c_str() + 1; + const char *msg = "Integer expected"; + const char *p = int_string; + char c; + // check that all chars in int_string are digits: + while ((c = *p++)) { + if (!isdigit(c)) { + parse_error(field, p - field.c_str() - 1, msg); + return 0; + } + } + p--; // p now points to end-of-string character + if (p - int_string == 0) { + // bad: string length is zero + parse_error(field, 1, msg); + return 0; + } + return atoi(int_string); +} + + +int Alg_reader::find_real_in(string &field, int n) +{ + // scans from offset n to the end of a real constant + bool decimal = false; + int len = field.length(); + if (n < len && field[n] == '-') n += 1; // parse one minus sign + for (int i = n; i < len; i++) { + char c = field[i]; + if (!isdigit(c)) { + if (c == '.' && !decimal) { + decimal = true; + } else { + return i; + } + } + } + return len; +} + + +double Alg_reader::parse_real(string &field) +{ + const char *msg = "Real expected"; + int last = find_real_in(field, 1); + string real_string = field.substr(1, last - 1); + if (last <= 1 || last < (int) field.length()) { + parse_error(field, 1, msg); + return 0; + } + return atof(real_string.c_str()); +} + + +void Alg_reader::parse_error(string &field, long offset, char *message) +{ + int position = line_parser.pos - field.length() + offset; + error_flag = true; + puts(line_parser.str->c_str()); + for (int i = 0; i < position; i++) { + putc(' ', stdout); + } + putc('^', stdout); + printf(" %s\n", message); +} + +void Alg_reader::parse_error(string &field, long offset, const char *message) +{ + parse_error(field, offset, const_cast(message)); +} + + +double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; + + +double Alg_reader::parse_dur(string &field, double base) +{ + const char *msg = "Duration expected"; + const char *durs = "SIQHW"; + const char *p; + int last; + double dur; + if (field.length() < 2) { + // fall through to error message + return -1; + } else if (isdigit(field[1])) { + last = find_real_in(field, 1); + string real_string = field.substr(1, last - 1); + dur = atof(real_string.c_str()); + // convert dur from seconds to beats + dur = seq->get_time_map()->time_to_beat(base + dur) - + seq->get_time_map()->time_to_beat(base); + } else if ((p = strchr(durs, toupper(field[1])))) { + dur = duration_lookup[p - durs]; + last = 2; + } else { + parse_error(field, 1, msg); + return 0; + } + dur = parse_after_dur(dur, field, last, base); + dur = seq->get_time_map()->beat_to_time( + seq->get_time_map()->time_to_beat(base) + dur) - base; + return dur; +} + + +double Alg_reader::parse_after_dur(double dur, string &field, + int n, double base) +{ + if ((int) field.length() == n) { + return dur; + } + if (toupper(field[n]) == 'T') { + return parse_after_dur(dur * 2/3, field, n + 1, base); + } + if (field[n] == '.') { + return parse_after_dur(dur * 1.5, field, n + 1, base); + } + if (isdigit(field[n])) { + int last = find_real_in(field, n); + string a_string = field.substr(n, last - n); + double f = atof(a_string.c_str()); + return parse_after_dur(dur * f, field, last, base); + } + if (field[n] == '+') { + string a_string = field.substr(n + 1); + return dur + parse_dur( + a_string, seq->get_time_map()->beat_to_time( + seq->get_time_map()->time_to_beat(base) + dur)); + } + parse_error(field, n, "Unexpected character in duration"); + return dur; +} + +struct loud_lookup_struct { + const char *str; + int val; +} loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100}, + {"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60}, + {NULL, 0} }; + + +double Alg_reader::parse_loud(string &field) +{ + const char *msg = "Loudness expected"; + if (isdigit(field[1])) { + return parse_int(field); + } else { + string dyn = field.substr(1); + transform(dyn.begin(), dyn.end(), dyn.begin(), ::toupper); + for (int i = 0; loud_lookup[i].str; i++) { + if (streql(loud_lookup[i].str, dyn.c_str())) { + return (double) loud_lookup[i].val; + } + } + } + parse_error(field, 1, msg); + return 100.0; +} + + +int key_lookup[] = {21, 23, 12, 14, 16, 17, 19}; + + +// the field can be K or K[A-G] or P[A-G] +// (this can be called from parse_pitch() to handle [A-G]) +// Notice that the routine ignores the first character: K or P +// +long Alg_reader::parse_key(string &field) +{ + const char *msg = "Pitch expected"; + const char *pitches = "ABCDEFG"; + const char *p; + if (isdigit(field[1])) { + // This routine would not have been called if field = "P" + // so it must be "K" so must be an integer. + return parse_int(field); + } else if ((p = strchr(pitches, toupper(field[1])))) { + long key = key_lookup[p - pitches]; + key = parse_after_key(key, field, 2); + return key; + } + parse_error(field, 1, msg); + return 0; +} + + +long Alg_reader::parse_after_key(int key, string &field, int n) +{ + if ((int) field.length() == n) { + return key; + } + char c = toupper(field[n]); + if (c == 'S') { + return parse_after_key(key + 1, field, n + 1); + } + if (c == 'F') { + return parse_after_key(key - 1, field, n + 1); + } + if (isdigit(field[n])) { + int last = find_int_in(field, n); + string octave = field.substr(n, last - n); + int oct = atoi(octave.c_str()); + return parse_after_key(key + oct * 12, field, last); + } + parse_error(field, n, "Unexpected character in pitch"); + return key; +} + + +long Alg_reader::find_int_in(string &field, int n) +{ + while ((int) field.length() > n && isdigit(field[n])) { + n = n + 1; + } + return n; +} + + +bool Alg_reader::parse_attribute(string &field, Alg_parameter_ptr param) +{ + int i = 1; + while (i < (int) field.length()) { + if (field[i] == ':') { + string attr = field.substr(1, i - 1); + char type_char = field[i - 1]; + if (strchr("iarsl", type_char)) { + param->set_attr(symbol_table.insert_string(attr.c_str())); + parse_val(param, field, i + 1); + } else { + parse_error(field, 0, "attribute needs to end with typecode: i,a,r,s, or l"); + } + return !error_flag; + } + i = i + 1; + } + return false; +} + + +bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i) +{ + int len = (int) s.length(); + if (i >= len) { + return false; + } + if (s[i] == '"') { + if (!check_type('s', param)) { + return false; + } + // note: (len - i) includes 2 quote characters but no EOS character + // so total memory to allocate is (len - i) - 1 + char *r = new char[(len - i) - 1]; + strncpy(r, s.c_str() + i + 1, (len - i) - 2); + r[(len - i) - 2] = 0; // terminate the string + param->s = r; + } else if (s[i] == '\'') { + if (!check_type('a', param)) { + return false; + } + string r = s.substr(i + 1, len - i - 2); + param->a = symbol_table.insert_string(r.c_str()); + } else if (param->attr_type() == 'l') { + if (streql(s.c_str() + i, "true") || + streql(s.c_str() + i, "t")) { + param->l = true; + } else if (streql(s.c_str() + i, "false") || + streql(s.c_str() + i, "nil")) { + param->l = false; + } else return false; + } else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') { + int pos = i; + bool period = false; + /* int sign = 1; LMMS unused variable */ + if (s[pos] == '-') { + /* sign = -1; LMMS unused variable */ + pos++; + } + while (pos < len) { + if (isdigit(s[pos])) { + ; + } else if (!period && s[pos] == '.') { + period = true; + } else { + parse_error(s, pos, "Unexpected char in number"); + return false; + } + pos = pos + 1; + } + string r = s.substr(i, len - i); + if (period) { + if (!check_type('r', param)) { + return false; + } + param->r = atof(r.c_str()); + } else { + if (param->attr_type() == 'r') { + param->r = atoi(r.c_str()); + } else if (!check_type('i', param)) { + return false; + } else { + param->i = atoi(r.c_str()); + } + } + } else { + parse_error(s, i, "invalid value"); + return false; + } + return true; +} + + +bool Alg_reader::check_type(char type_char, Alg_parameter_ptr param) +{ + return param->attr_type() == type_char; +} + + +//duration_lookup = {"S": 0.5, "I": 0.5, "Q": 1, "H": 2, "W": 4} +//key_lookup = {"C": 12, "D": 14, "E": 16, "F": 17, "G": 19, "A": 21, "B": 23} + +/* +def test(): + reader = Alg_reader(open("data\\test.gro", "r")) + reader.parse() + score = reader->seq.notes + print "score:", score + reader = nil +*/ diff --git a/plugins/MidiImport/portsmf/allegrosmfrd.cpp b/plugins/MidiImport/portsmf/allegrosmfrd.cpp index 49e2ef03ee6..279462762a7 100644 --- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfrd.cpp @@ -1,445 +1,461 @@ -// midifile reader - -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "debug.h" -#include -#include -#include "allegro.h" -#include "algsmfrd_internal.h" -#include "mfmidi.h" -#include "trace.h" - -using namespace std; - -typedef class Alg_pending { -public: - Alg_note_ptr note; - class Alg_pending *next; - Alg_pending(Alg_note_ptr n, class Alg_pending *list) { - note = n; next = list; } -} *Alg_pending_ptr; - - -class Alg_midifile_reader: public Midifile_reader { -public: - istream *file; - Alg_seq_ptr seq; - int divisions; - Alg_pending_ptr pending; - Alg_track_ptr track; - int track_number; // the number of the (current) track - // chan is actual_channel + channel_offset_per_track * track_num + - // channel_offset_per_track * port - long channel_offset_per_track; // used to encode track number into channel - // default is 0, set this to 0 to merge all tracks to 16 channels - long channel_offset_per_port; // used to encode port number into channel - // default is 16, set to 0 to ignore port prefix meta events - // while reading, this is channel_offset_per_track * track_num - int channel_offset; - - Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) { - file = &f; - pending = NULL; - seq = new_seq; - channel_offset_per_track = 0; - channel_offset_per_port = 16; - track_number = -1; // no tracks started yet, 1st will be #0 - meta_channel = -1; - port = 0; - } - // delete destroys the seq member as well, so set it to NULL if you - // copied the pointer elsewhere - ~Alg_midifile_reader(); - // the following is used to load the Alg_seq from the file: - bool parse(); - - void set_nomerge(bool flag) { Mf_nomerge = flag; } - void set_skipinit(bool flag) { Mf_skipinit = flag; } - long get_currtime() { return Mf_currtime; } - -protected: - int meta_channel; // the channel for meta events, set by MIDI chan prefix - int port; // value from the portprefix meta event - - double get_time(); - void update(int chan, int key, Alg_parameter_ptr param); - void *Mf_malloc(size_t size) { return malloc(size); } - void Mf_free(void *obj, size_t size) { free(obj); } - /* Methods to be called while processing the MIDI file. */ - void Mf_starttrack(); - void Mf_endtrack(); - int Mf_getc(); - void Mf_chanprefix(int chan); - void Mf_portprefix(int port); - void Mf_eot(); - void Mf_error(const char *); - void Mf_header(int,int,int); - void Mf_on(int,int,int); - void Mf_off(int,int,int); - void Mf_pressure(int,int,int); - void Mf_controller(int,int,int); - void Mf_pitchbend(int,int,int); - void Mf_program(int,int); - void Mf_chanpressure(int,int); - void binary_msg(int len, char *msg, const char *attr_string); - void Mf_sysex(int,char*); - void Mf_arbitrary(int,char*); - void Mf_metamisc(int,int,char*); - void Mf_seqnum(int); - void Mf_smpte(int,int,int,int,int); - void Mf_timesig(int,int,int,int); - void Mf_tempo(int); - void Mf_keysig(int,int); - void Mf_sqspecific(int,char*); - void Mf_text(int,int,char*); -}; - - -Alg_midifile_reader::~Alg_midifile_reader() -{ - while (pending) { - Alg_pending_ptr to_be_freed = pending; - pending = pending->next; - delete to_be_freed; - } - finalize(); // free Mf reader memory -} - - -bool Alg_midifile_reader::parse() -{ - channel_offset = 0; - seq->convert_to_beats(); - midifile(); - seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur())); - return midifile_error != 0; -} - - -void Alg_midifile_reader::Mf_starttrack() -{ - // printf("starting new track\n"); - // create a new track that will share the sequence time map - // since time is in beats, the seconds parameter is false - track_number++; - seq->add_track(track_number); // make sure track exists - track = seq->track(track_number); // keep pointer to current track - meta_channel = -1; - port = 0; -} - - -void Alg_midifile_reader::Mf_endtrack() -{ - // note: track is already part of seq, so do not add it here - // printf("finished track, length %d number %d\n", track->len, track_num / 100); - channel_offset += seq->channel_offset_per_track; - track = NULL; - double now = get_time(); - if (seq->get_beat_dur() < now) seq->set_beat_dur(now); - meta_channel = -1; - port = 0; -} - - -int Alg_midifile_reader::Mf_getc() -{ - return file->get(); -} - - -void Alg_midifile_reader::Mf_chanprefix(int chan) -{ - meta_channel = chan; -} - - -void Alg_midifile_reader::Mf_portprefix(int p) -{ - port = p; -} - - -void Alg_midifile_reader::Mf_eot() -{ - meta_channel = -1; - port = 0; -} - - -void Alg_midifile_reader::Mf_error(const char *msg) -{ - fprintf(stdout, "Midifile reader error: %s\n", msg); -} - - -void Alg_midifile_reader::Mf_header(int format, int ntrks, int division) -{ - if (format > 1) { - char msg[80]; - sprintf(msg, "file format %d not implemented", format); - Mf_error(msg); - } - divisions = division; -} - - -double Alg_midifile_reader::get_time() -{ - double beat = ((double) get_currtime()) / divisions; - return beat; -} - - -void Alg_midifile_reader::Mf_on(int chan, int key, int vel) -{ - assert(!seq->get_units_are_seconds()); - if (vel == 0) { - Mf_off(chan, key, vel); - return; - } - Alg_note_ptr note = new Alg_note(); - pending = new Alg_pending(note, pending); - /* trace("on: %d at %g\n", key, get_time()); */ - note->time = get_time(); - note->chan = chan + channel_offset + port * channel_offset_per_port; - note->dur = 0; - note->set_identifier(key); - note->pitch = (float) key; - note->loud = (float) vel; - track->append(note); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_off(int chan, int key, int vel) -{ - double time = get_time(); - Alg_pending_ptr *p = &pending; - while (*p) { - if ((*p)->note->get_identifier() == key && - (*p)->note->chan == - chan + channel_offset + port * channel_offset_per_port) { - (*p)->note->dur = time - (*p)->note->time; - // trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur); - Alg_pending_ptr to_be_freed = *p; - *p = to_be_freed->next; - delete to_be_freed; - } else { - p = &((*p)->next); - } - } - meta_channel = -1; -} - - -void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param) -{ - Alg_update_ptr update = new Alg_update; - update->time = get_time(); - update->chan = chan; - if (chan != -1) { - update->chan = chan + channel_offset + port * channel_offset_per_port; - } - update->set_identifier(key); - update->parameter = *param; - // prevent the destructor from destroying the string twice! - // the new Update takes the string from param - if (param->attr_type() == 's') param->s = NULL; - track->append(update); -} - - -void Alg_midifile_reader::Mf_pressure(int chan, int key, int val) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("pressurer")); - parameter.r = val / 127.0; - update(chan, key, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_controller(int chan, int control, int val) -{ - Alg_parameter parameter; - char name[32]; - sprintf(name, "control%dr", control); - parameter.set_attr(symbol_table.insert_string(name)); - parameter.r = val / 127.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("bendr")); - parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_program(int chan, int program) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("programi")); - parameter.i = program; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_chanpressure(int chan, int val) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("pressurer")); - parameter.r = val / 127.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::binary_msg(int len, char *msg, - const char *attr_string) -{ - Alg_parameter parameter; - char *hexstr = new char[len * 2 + 1]; - for (int i = 0; i < len; i++) { - sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i])); - } - parameter.s = hexstr; - parameter.set_attr(symbol_table.insert_string(attr_string)); - update(meta_channel, -1, ¶meter); -} - - -void Alg_midifile_reader::Mf_sysex(int len, char *msg) -{ - // sysex messages become updates with attribute sysexs and a hex string - binary_msg(len, msg, "sysexs"); -} - - -void Alg_midifile_reader::Mf_arbitrary(int len, char *msg) -{ - Mf_error("arbitrary data ignored"); -} - - -void Alg_midifile_reader::Mf_metamisc(int type, int len, char *msg) -{ - char text[128]; - sprintf(text, "metamsic data, type 0x%x, ignored", type); - Mf_error(text); -} - - -void Alg_midifile_reader::Mf_seqnum(int n) -{ - Mf_error("seqnum data ignored"); -} - - -static const char *fpsstr[4] = {"24", "25", "29.97", "30"}; - -void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs, - int frames, int subframes) -{ - // string will look like "24fps:01h:27m:07s:19.00f" - // 30fps (drop frame) is notated as "29.97fps" - char text[32]; - int fps = (hours >> 6) & 3; - hours &= 0x1F; - sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df", - fpsstr[fps], hours, mins, secs, frames, subframes); - Alg_parameter smpteoffset; - smpteoffset.s = heapify(text); - smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets")); - update(meta_channel, -1, &smpteoffset); - // Mf_error("SMPTE data ignored"); -} - - -void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4) -{ - seq->set_time_sig(get_currtime() / divisions, i1, 1 << i2); -} - - -void Alg_midifile_reader::Mf_tempo(int tempo) -{ - double beat = get_currtime(); - beat = beat / divisions; // convert to quarters - // 6000000 us/min / n us/beat => beat / min - double bpm = 60000000.0 / tempo; - seq->insert_tempo(bpm, beat); -} - - -void Alg_midifile_reader::Mf_keysig(int key, int mode) -{ - Alg_parameter key_parm; - key_parm.set_attr(symbol_table.insert_string("keysigi")); - // use 0 for C major, 1 for G, -1 for F, etc., that is, - // the number of sharps, where flats are negative sharps - key_parm.i = key; //<<<---- fix this - // use -1 to mean "all channels" - update(meta_channel, -1, &key_parm); - Alg_parameter mode_parm; - mode_parm.set_attr(symbol_table.insert_string("modea")); - mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") : - symbol_table.insert_string("minor")); - update(meta_channel, -1, &mode_parm); -} - - -void Alg_midifile_reader::Mf_sqspecific(int len, char *msg) -{ - // sequencer specific messages become updates with attribute sqspecifics - // and a hex string for the value - binary_msg(len, msg, "sqspecifics"); -} - - -char *heapify2(int len, char *s) -{ - char *h = new char[len + 1]; - memcpy(h, s, len); - h[len] = 0; - return h; -} - - -void Alg_midifile_reader::Mf_text(int type, int len, char *msg) -{ - Alg_parameter text; - text.s = heapify2(len, msg); - const char *attr = "miscs"; - if (type == 1) attr = "texts"; - else if (type == 2) attr = "copyrights"; - else if (type == 3) - attr = (track_number == 0 ? "seqnames" : "tracknames"); - else if (type == 4) attr = "instruments"; - else if (type == 5) attr = "lyrics"; - else if (type == 6) attr = "markers"; - else if (type == 7) attr = "cues"; - text.set_attr(symbol_table.insert_string(attr)); - update(meta_channel, -1, &text); -} - - -// parse file into a seq. -Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq) -{ - assert(new_seq); - Alg_midifile_reader ar(file, new_seq); - bool err = ar.parse(); - ar.seq->set_real_dur(ar.seq->get_time_map()-> - beat_to_time(ar.seq->get_beat_dur())); - return (err ? alg_error_syntax : alg_no_error); -} +// midifile reader + +#include "stdlib.h" +#include "stdio.h" +#include "string.h" +#include "assert.h" +#include +#include +#include "allegro.h" +#include "algsmfrd_internal.h" +#include "mfmidi.h" +#include "trace.h" + +using namespace std; + +typedef class Alg_note_list { +public: + Alg_note_ptr note; + class Alg_note_list *next; + Alg_note_list(Alg_note_ptr n, class Alg_note_list *list) { + note = n; next = list; } +} *Alg_note_list_ptr; + + +class Alg_midifile_reader: public Midifile_reader { +public: + istream *file; + Alg_seq_ptr seq; + int divisions; + Alg_note_list_ptr note_list; + Alg_track_ptr track; + int track_number; // the number of the (current) track + // chan is actual_channel + channel_offset_per_track * track_num + + // channel_offset_per_track * port + long channel_offset_per_track; // used to encode track number into channel + // default is 0, set this to 0 to merge all tracks to 16 channels + long channel_offset_per_port; // used to encode port number into channel + // default is 16, set to 0 to ignore port prefix meta events + // while reading, this is channel_offset_per_track * track_num + int channel_offset; + + Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) { + file = &f; + note_list = NULL; + seq = new_seq; + channel_offset_per_track = 0; + channel_offset_per_port = 16; + track_number = -1; // no tracks started yet, 1st will be #0 + meta_channel = -1; + port = 0; + } + // delete destroys the seq member as well, so set it to NULL if you + // copied the pointer elsewhere + ~Alg_midifile_reader(); + // the following is used to load the Alg_seq from the file: + bool parse(); + + void set_nomerge(bool flag) { Mf_nomerge = flag; } + void set_skipinit(bool flag) { Mf_skipinit = flag; } + long get_currtime() { return Mf_currtime; } + +protected: + int meta_channel; // the channel for meta events, set by MIDI chan prefix + int port; // value from the portprefix meta event + + double get_time(); + void update(int chan, int key, Alg_parameter_ptr param); + void *Mf_malloc(size_t size) { return malloc(size); } + void Mf_free(void *obj, size_t size) { free(obj); } + /* Methods to be called while processing the MIDI file. */ + void Mf_starttrack(); + void Mf_endtrack(); + int Mf_getc(); + void Mf_chanprefix(int chan); + void Mf_portprefix(int port); + void Mf_eot(); + void Mf_error(char *); + void Mf_error(const char *); + void Mf_header(int,int,int); + void Mf_on(int,int,int); + void Mf_off(int,int,int); + void Mf_pressure(int,int,int); + void Mf_controller(int,int,int); + void Mf_pitchbend(int,int,int); + void Mf_program(int,int); + void Mf_chanpressure(int,int); + void binary_msg(int len, unsigned char *msg, const char *attr_string); + void Mf_sysex(int,unsigned char*); + void Mf_arbitrary(int,unsigned char*); + void Mf_metamisc(int,int,unsigned char*); + void Mf_seqnum(int); + void Mf_smpte(int,int,int,int,int); + void Mf_timesig(int,int,int,int); + void Mf_tempo(int); + void Mf_keysig(int,int); + void Mf_sqspecific(int,unsigned char*); + void Mf_text(int,int,unsigned char*); +}; + + +Alg_midifile_reader::~Alg_midifile_reader() +{ + while (note_list) { + Alg_note_list_ptr to_be_freed = note_list; + note_list = note_list->next; + delete to_be_freed; + } + finalize(); // free Mf reader memory +} + + +bool Alg_midifile_reader::parse() +{ + channel_offset = 0; + seq->convert_to_beats(); + midifile(); + seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur())); + return midifile_error != 0; +} + + +void Alg_midifile_reader::Mf_starttrack() +{ + // printf("starting new track\n"); + // create a new track that will share the sequence time map + // since time is in beats, the seconds parameter is false + track_number++; + seq->add_track(track_number); // make sure track exists + track = seq->track(track_number); // keep pointer to current track + meta_channel = -1; + port = 0; +} + + +void Alg_midifile_reader::Mf_endtrack() +{ + // note: track is already part of seq, so do not add it here + // printf("finished track, length %d number %d\n", track->len, track_num / 100); + channel_offset += seq->channel_offset_per_track; + track = NULL; + double now = get_time(); + if (seq->get_beat_dur() < now) seq->set_beat_dur(now); + meta_channel = -1; + port = 0; +} + + +int Alg_midifile_reader::Mf_getc() +{ + return file->get(); +} + + +void Alg_midifile_reader::Mf_chanprefix(int chan) +{ + meta_channel = chan; +} + + +void Alg_midifile_reader::Mf_portprefix(int p) +{ + port = p; +} + + +void Alg_midifile_reader::Mf_eot() +{ + meta_channel = -1; + port = 0; +} + + +void Alg_midifile_reader::Mf_error(char *msg) +{ + fprintf(stdout, "Midifile reader error: %s\n", msg); +} + +void Alg_midifile_reader::Mf_error(const char *msg) +{ + Mf_error(const_cast(msg)); +} + + +void Alg_midifile_reader::Mf_header(int format, int ntrks, int division) +{ + if (format > 1) { + char msg[80]; +//#pragma warning(disable: 4996) // msg is long enough + sprintf(msg, "file format %d not implemented", format); +//#pragma warning(default: 4996) + Mf_error(msg); + } + divisions = division; +} + + +double Alg_midifile_reader::get_time() +{ + double beat = ((double) get_currtime()) / divisions; + return beat; +} + + +void Alg_midifile_reader::Mf_on(int chan, int key, int vel) +{ + assert(!seq->get_units_are_seconds()); + if (vel == 0) { + Mf_off(chan, key, vel); + return; + } + Alg_note_ptr note = new Alg_note(); + note_list = new Alg_note_list(note, note_list); + /* trace("on: %d at %g\n", key, get_time()); */ + note->time = get_time(); + note->chan = chan + channel_offset + port * channel_offset_per_port; + note->dur = 0; + note->set_identifier(key); + note->pitch = (float) key; + note->loud = (float) vel; + track->append(note); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_off(int chan, int key, int vel) +{ + double time = get_time(); + Alg_note_list_ptr *p = ¬e_list; + while (*p) { + if ((*p)->note->get_identifier() == key && + (*p)->note->chan == + chan + channel_offset + port * channel_offset_per_port) { + (*p)->note->dur = time - (*p)->note->time; + // trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur); + Alg_note_list_ptr to_be_freed = *p; + *p = to_be_freed->next; + delete to_be_freed; + } else { + p = &((*p)->next); + } + } + meta_channel = -1; +} + + +void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param) +{ + Alg_update_ptr update = new Alg_update; + update->time = get_time(); + update->chan = chan; + if (chan != -1) { + update->chan = chan + channel_offset + port * channel_offset_per_port; + } + update->set_identifier(key); + update->parameter = *param; + // prevent the destructor from destroying the string twice! + // the new Update takes the string from param + if (param->attr_type() == 's') param->s = NULL; + track->append(update); +} + + +void Alg_midifile_reader::Mf_pressure(int chan, int key, int val) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("pressurer")); + parameter.r = val / 127.0; + update(chan, key, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_controller(int chan, int control, int val) +{ + Alg_parameter parameter; + char name[32]; +//#pragma warning(disable: 4996) // name is long enough + sprintf(name, "control%dr", control); +//#pragma warning(default: 4996) + parameter.set_attr(symbol_table.insert_string(name)); + parameter.r = val / 127.0; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("bendr")); + parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_program(int chan, int program) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("programi")); + parameter.i = program; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_chanpressure(int chan, int val) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("pressurer")); + parameter.r = val / 127.0; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::binary_msg(int len, unsigned char *msg, + const char *attr_string) +{ + Alg_parameter parameter; + char *hexstr = new char[len * 2 + 1]; + for (int i = 0; i < len; i++) { +//#pragma warning(disable: 4996) // hexstr is long enough + sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i])); +//#pragma warning(default: 4996) + } + parameter.s = hexstr; + parameter.set_attr(symbol_table.insert_string(attr_string)); + update(meta_channel, -1, ¶meter); +} + + +void Alg_midifile_reader::Mf_sysex(int len, unsigned char *msg) +{ + // sysex messages become updates with attribute sysexs and a hex string + binary_msg(len, msg, "sysexs"); +} + + +void Alg_midifile_reader::Mf_arbitrary(int len, unsigned char *msg) +{ + Mf_error("arbitrary data ignored"); +} + + +void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg) +{ + char text[128]; +//#pragma warning(disable: 4996) // text is long enough + sprintf(text, "metamsic data, type 0x%x, ignored", type); +//#pragma warning(default: 4996) + Mf_error(text); +} + + +void Alg_midifile_reader::Mf_seqnum(int n) +{ + Mf_error("seqnum data ignored"); +} + + +static const char *fpsstr[4] = {"24", "25", "29.97", "30"}; + +void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs, + int frames, int subframes) +{ + // string will look like "24fps:01h:27m:07s:19.00f" + // 30fps (drop frame) is notated as "29.97fps" + char text[32]; + int fps = (hours >> 6) & 3; + hours &= 0x1F; +//#pragma warning(disable: 4996) // text is long enough + sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df", + fpsstr[fps], hours, mins, secs, frames, subframes); +//#pragma warning(default: 4996) + Alg_parameter smpteoffset; + smpteoffset.s = heapify(text); + smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets")); + update(meta_channel, -1, &smpteoffset); + // Mf_error("SMPTE data ignored"); +} + + +void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4) +{ + seq->set_time_sig(double(get_currtime()) / divisions, i1, 1 << i2); +} + + +void Alg_midifile_reader::Mf_tempo(int tempo) +{ + double beat = get_currtime(); + beat = beat / divisions; // convert to quarters + // 6000000 us/min / n us/beat => beat / min + double bpm = 60000000.0 / tempo; + seq->insert_tempo(bpm, beat); +} + + +void Alg_midifile_reader::Mf_keysig(int key, int mode) +{ + Alg_parameter key_parm; + key_parm.set_attr(symbol_table.insert_string("keysigi")); + // use 0 for C major, 1 for G, -1 for F, etc., that is, + // the number of sharps, where flats are negative sharps + key_parm.i = key; //<<<---- fix this + // use -1 to mean "all channels" + update(meta_channel, -1, &key_parm); + Alg_parameter mode_parm; + mode_parm.set_attr(symbol_table.insert_string("modea")); + mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") : + symbol_table.insert_string("minor")); + update(meta_channel, -1, &mode_parm); +} + + +void Alg_midifile_reader::Mf_sqspecific(int len, unsigned char *msg) +{ + // sequencer specific messages become updates with attribute sqspecifics + // and a hex string for the value + binary_msg(len, msg, "sqspecifics"); +} + + +char *heapify2(int len, unsigned char *s) +{ + char *h = new char[len + 1]; + memcpy(h, s, len); + h[len] = 0; + return h; +} + + +void Alg_midifile_reader::Mf_text(int type, int len, unsigned char *msg) +{ + Alg_parameter text; + text.s = heapify2(len, msg); + const char *attr = "miscs"; + if (type == 1) attr = "texts"; + else if (type == 2) attr = "copyrights"; + else if (type == 3) + attr = (track_number == 0 ? "seqnames" : "tracknames"); + else if (type == 4) attr = "instruments"; + else if (type == 5) attr = "lyrics"; + else if (type == 6) attr = "markers"; + else if (type == 7) attr = "cues"; + text.set_attr(symbol_table.insert_string(attr)); + update(meta_channel, -1, &text); +} + + +// parse file into a seq. +Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq) +{ + assert(new_seq); + Alg_midifile_reader ar(file, new_seq); + bool err = ar.parse(); + ar.seq->set_real_dur(ar.seq->get_time_map()-> + beat_to_time(ar.seq->get_beat_dur())); + return (err ? alg_error_syntax : alg_no_error); +} diff --git a/plugins/MidiImport/portsmf/allegrosmfwr.cpp b/plugins/MidiImport/portsmf/allegrosmfwr.cpp index 5a76c44ed9f..7cac9a0412f 100644 --- a/plugins/MidiImport/portsmf/allegrosmfwr.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfwr.cpp @@ -34,7 +34,7 @@ class Alg_smf_write { // chan is actual_channel + channels_per_track * track_number // default is 100, set this to 0 to merge all tracks to 16 channels - void write(ofstream &file /* , midiFileFormat = 1 */); + void write(ostream &file /* , midiFileFormat = 1 */); private: long previous_divs; // time in ticks of most recently written event @@ -46,7 +46,7 @@ class Alg_smf_write { void write_note(Alg_note_ptr note, bool on); void write_update(Alg_update_ptr update); void write_text(Alg_update_ptr update, char type); - void write_binary(int type_byte, char *msg); + void write_binary(int type_byte, const char *msg); void write_midi_channel_prefix(Alg_update_ptr update); void write_smpteoffset(Alg_update_ptr update, char *s); void write_data(int data); @@ -162,7 +162,7 @@ void Alg_smf_write::write_note(Alg_note_ptr note, bool on) //printf("deltaDivisions: %d, beats elapsed: %g, on? %c\n", deltaDivisions, note->time, on); - char chan = (note->chan & 15); + char chan = char(note->chan & 15); int pitch = int(note->pitch + 0.5); if (pitch < 0) { pitch = pitch % 12; @@ -184,8 +184,8 @@ void Alg_smf_write::write_midi_channel_prefix(Alg_update_ptr update) { if (update->chan >= 0) { // write MIDI Channel Prefix write_delta(update->time); - out_file->put(0xFF); // Meta Event - out_file->put(0x20); // Type code for MIDI Channel Prefix + out_file->put('\xFF'); // Meta Event + out_file->put('\x20'); // Type code for MIDI Channel Prefix out_file->put(1); // length out_file->put(to_midi_channel(update->chan)); // one thing odd about the Std MIDI File spec is that once @@ -201,7 +201,7 @@ void Alg_smf_write::write_text(Alg_update_ptr update, char type) { write_midi_channel_prefix(update); write_delta(update->time); - out_file->put(0xFF); + out_file->put('\xFF'); out_file->put(type); out_file->put((char) strlen(update->parameter.s)); *out_file << update->parameter.s; @@ -212,8 +212,8 @@ void Alg_smf_write::write_smpteoffset(Alg_update_ptr update, char *s) { write_midi_channel_prefix(update); write_delta(update->time); - out_file->put(0xFF); // meta event - out_file->put(0x54); // smpte offset type code + out_file->put('\xFF'); // meta event + out_file->put('\x54'); // smpte offset type code out_file->put(5); // length for (int i = 0; i < 5; i++) *out_file << s[i]; } @@ -255,13 +255,13 @@ static char hex_to_nibble(char c) } -static char hex_to_char(char *s) +static char hex_to_char(const char *s) { return (hex_to_nibble(s[0]) << 4) + hex_to_nibble(s[1]); } -void Alg_smf_write::write_binary(int type_byte, char *msg) +void Alg_smf_write::write_binary(int type_byte, const char *msg) { int len = strlen(msg) / 2; out_file->put(type_byte); @@ -275,7 +275,7 @@ void Alg_smf_write::write_binary(int type_byte, char *msg) void Alg_smf_write::write_update(Alg_update_ptr update) { - char *name = update->parameter.attr_name(); + const char *name = update->parameter.attr_name(); /****Non-Meta Events****/ if (!strcmp(name, "pressurer")) { @@ -312,7 +312,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) write_data(val); } else if (!strcmp(name, "sysexs") && update->parameter.attr_type() == 's') { - char *s = update->parameter.s; + const char *s = update->parameter.s; if (s[0] && s[1] && toupper(s[0]) == 'F' && s[1] == '0') { s += 2; // skip the initial "F0" byte in message: it is implied } @@ -320,9 +320,9 @@ void Alg_smf_write::write_update(Alg_update_ptr update) write_binary(0xF0, s); } else if (!strcmp(name, "sqspecifics") && update->parameter.attr_type() == 's') { - char *s = update->parameter.s; + const char *s = update->parameter.s; write_delta(update->time); - out_file->put(0xFF); + out_file->put('\xFF'); write_binary(0x7F, s); /****Text Events****/ @@ -349,7 +349,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) // smpteoffset is specified as "24fps:00h:10m:00s:11.00f" // the following simple parser does not reject all badly // formatted strings, but it should parse good strings ok - char *s = update->parameter.s; + const char *s = update->parameter.s; int len = strlen(s); char smpteoffset[5]; if (len < 24) return; // not long enough, must be bad format @@ -390,8 +390,8 @@ void Alg_smf_write::write_update(Alg_update_ptr update) } if (keysig != -99 && keysig_mode) { // write when both are defined write_delta(keysig_when); - out_file->put(0xFF); - out_file->put(0x59); + out_file->put('\xFF'); + out_file->put('\x59'); out_file->put(2); // mask off high bits so that this value appears to be positive // i.e. -1 -> 0xFF (otherwise, write_data will clip -1 to 0) @@ -482,9 +482,9 @@ void Alg_smf_write::write_tempo(int divs, int tempo) // printf("Inserting tempo %f after %f clocks.\n", tempo, delta); write_varinum(divs - previous_divs); previous_divs = divs; - out_file->put(0xFF); - out_file->put(0x51); - out_file->put(0x03); + out_file->put('\xFF'); + out_file->put('\x51'); + out_file->put('\x03'); write_24bit((int)tempo); } @@ -512,12 +512,11 @@ void Alg_smf_write::write_tempo_change(int i) void Alg_smf_write::write_time_signature(int i) { Alg_time_sigs &ts = seq->time_sig; + write_delta(ts[i].beat); // write the time signature - long divs = ROUND(ts[i].beat * division); - write_varinum(divs - previous_divs); - out_file->put(0xFF); - out_file->put(0x58); // time signature - out_file->put(4); // length of message + out_file->put('\xFF'); + out_file->put('\x58'); // time signature + out_file->put('\x04'); // length of message out_file->put(ROUND(ts[i].num)); int den = ROUND(ts[i].den); int den_byte = 0; @@ -532,7 +531,7 @@ void Alg_smf_write::write_time_signature(int i) -void Alg_smf_write::write(ofstream &file) +void Alg_smf_write::write(ostream &file) { int track_len_offset; int track_end_offset; @@ -564,9 +563,9 @@ void Alg_smf_write::write(ofstream &file) // End of track event write_varinum(0); // delta time - out_file->put(0xFF); - out_file->put(0x2F); - out_file->put(0x00); + out_file->put('\xFF'); + out_file->put('\x2F'); + out_file->put('\x00'); // Go back and write in the length of the track track_end_offset = out_file->tellp(); @@ -632,7 +631,7 @@ void Alg_smf_write::write_varinum(int value) } -void Alg_seq::smf_write(ofstream &file) +void Alg_seq::smf_write(ostream &file) { Alg_smf_write writer(this); writer.write(file); @@ -646,4 +645,3 @@ bool Alg_seq::smf_write(const char *filename) outf.close(); return true; } - diff --git a/plugins/MidiImport/portsmf/allegrowr.cpp b/plugins/MidiImport/portsmf/allegrowr.cpp index 3b266f84cac..3142cfd7307 100644 --- a/plugins/MidiImport/portsmf/allegrowr.cpp +++ b/plugins/MidiImport/portsmf/allegrowr.cpp @@ -1,6 +1,6 @@ // allegrowr.cpp -- write sequence to an Allegro file (text) -#include "debug.h" +#include "assert.h" #include "stdlib.h" #include #include @@ -56,32 +56,34 @@ Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n, // find a name and write it, return a pointer to it so the track // writer knows what update (if any) to skip { - Alg_event_ptr e = NULL; + Alg_event_ptr e = NULL; // e is the result, default is NULL file << "#track " << n; const char *attr = symbol_table.insert_string( n == 0 ? "seqnames" : "tracknames"); // search for name in events with timestamp of 0 for (int i = 0; i < events.length(); i++) { - e = events[i]; - if (e->time > 0) break; - if (e->is_update()) { - Alg_update_ptr u = (Alg_update_ptr) e; + Alg_event_ptr ue = events[i]; + if (ue->time > 0) break; + if (ue->is_update()) { + Alg_update_ptr u = (Alg_update_ptr) ue; if (u->parameter.attr == attr) { file << " " << u->parameter.s; + e = ue; // return the update event we found break; } } } - file << endl; - return e; + file << endl; // end of line containing #track [] + return e; // return parameter event with name if one was found } -void Alg_seq::write(ostream &file, bool in_secs) +void Alg_seq::write(ostream &file, bool in_secs, double offset) { int i, j; if (in_secs) convert_to_seconds(); else convert_to_beats(); + file << "#offset " << offset << endl; Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]); Alg_beats &beats = time_map->beats; for (i = 0; i < beats.len - 1; i++) { @@ -171,11 +173,11 @@ void Alg_seq::write(ostream &file, bool in_secs) } } -bool Alg_seq::write(const char *filename) +bool Alg_seq::write(const char *filename, double offset) { ofstream file(filename); if (file.fail()) return false; - write(file, units_are_seconds); - file.close(); - return true; + write(file, units_are_seconds, offset); + file.close(); + return true; } diff --git a/plugins/MidiImport/portsmf/mfmidi.cpp b/plugins/MidiImport/portsmf/mfmidi.cpp index 52f93b83764..7ea4b6ba357 100644 --- a/plugins/MidiImport/portsmf/mfmidi.cpp +++ b/plugins/MidiImport/portsmf/mfmidi.cpp @@ -13,6 +13,7 @@ #include "stdio.h" #include "mfmidi.h" #include "string.h" +#include "assert.h" #define MIDIFILE_ERROR -1 @@ -38,6 +39,7 @@ int Midifile_reader::readmt(const char *s, int skip) /* read through the "MThd" or "MTrk" header string */ /* if skip == 1, we attempt to skip initial garbage. */ { + assert(strlen(s) == 4); // must be "MThd" or "MTrk" int nread = 0; char b[4]; char buff[32]; @@ -66,8 +68,10 @@ int Midifile_reader::readmt(const char *s, int skip) goto retry; } err: +//#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths (void) strcpy(buff,errmsg); (void) strcat(buff,s); +//#pragma warning(default: 4996) // turn it back on mferror(buff); return(0); } @@ -189,7 +193,7 @@ void Midifile_reader::readtrack() msginit(); while ( Mf_toberead > lookfor ) { - char c = egetc(); + unsigned char c = egetc(); if (midifile_error) return; msgadd(c); } @@ -253,15 +257,17 @@ void Midifile_reader::readtrack() void Midifile_reader::badbyte(int c) { char buff[32]; - +//#pragma warning(disable: 4996) // safe in this case (void) sprintf(buff,"unexpected byte: 0x%02x",c); +//#pragma warning(default: 4996) mferror(buff); } void Midifile_reader::metaevent(int type) { int leng = msgleng(); - char *m = msg(); + // made this unsigned to avoid sign extend + unsigned char *m = msg(); switch ( type ) { case 0x00: @@ -408,12 +414,17 @@ int Midifile_reader::read16bit() return to16bit(c1,c2); } -void Midifile_reader::mferror(const char *s) +void Midifile_reader::mferror(char *s) { Mf_error(s); midifile_error = 1; } +void Midifile_reader::mferror(const char *s) +{ + mferror(const_cast(s)); +} + /* The code below allows collection of a system exclusive message of */ /* arbitrary length. The Msgbuff is expanded as necessary. The only */ /* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */ @@ -444,7 +455,7 @@ void Midifile_reader::msginit() Msgindex = 0; } -char *Midifile_reader::msg() +unsigned char *Midifile_reader::msg() { return(Msgbuff); } @@ -464,21 +475,16 @@ void Midifile_reader::msgadd(int c) void Midifile_reader::msgenlarge() { - char *newmess; - char *oldmess = Msgbuff; + unsigned char *newmess; + unsigned char *oldmess = Msgbuff; int oldleng = Msgsize; Msgsize += MSGINCREMENT; - newmess = (char *) Mf_malloc((sizeof(char) * Msgsize) ); + newmess = (unsigned char *) Mf_malloc((sizeof(unsigned char) * Msgsize) ); /* copy old message into larger new one */ if ( oldmess != 0 ) { - register char *p = newmess; - register char *q = oldmess; - register char *endq = &oldmess[oldleng]; - - for ( ; q!=endq ; p++,q++ ) - *p = *q; + memcpy(newmess, oldmess, oldleng); Mf_free(oldmess, oldleng); } Msgbuff = newmess; diff --git a/plugins/MidiImport/portsmf/mfmidi.h b/plugins/MidiImport/portsmf/mfmidi.h index d0049294bc2..744411e3bba 100644 --- a/plugins/MidiImport/portsmf/mfmidi.h +++ b/plugins/MidiImport/portsmf/mfmidi.h @@ -46,7 +46,7 @@ class Midifile_reader { virtual void Mf_chanprefix(int) = 0; virtual void Mf_portprefix(int) = 0; virtual void Mf_eot() = 0; - virtual void Mf_error(const char *) = 0; + virtual void Mf_error(char *) = 0; virtual void Mf_header(int,int,int) = 0; virtual void Mf_on(int,int,int) = 0; virtual void Mf_off(int,int,int) = 0; @@ -55,16 +55,16 @@ class Midifile_reader { virtual void Mf_pitchbend(int,int,int) = 0; virtual void Mf_program(int,int) = 0; virtual void Mf_chanpressure(int,int) = 0; - virtual void Mf_sysex(int,char*) = 0; - virtual void Mf_arbitrary(int,char*) = 0; - virtual void Mf_metamisc(int,int,char*) = 0; + virtual void Mf_sysex(int,unsigned char*) = 0; + virtual void Mf_arbitrary(int,unsigned char*) = 0; + virtual void Mf_metamisc(int,int,unsigned char*) = 0; virtual void Mf_seqnum(int) = 0; virtual void Mf_smpte(int,int,int,int,int) = 0; virtual void Mf_timesig(int,int,int,int) = 0; virtual void Mf_tempo(int) = 0; virtual void Mf_keysig(int,int) = 0; - virtual void Mf_sqspecific(int,char*) = 0; - virtual void Mf_text(int,int,char*) = 0; + virtual void Mf_sqspecific(int,unsigned char*) = 0; + virtual void Mf_text(int,int,unsigned char*) = 0; private: long Mf_toberead; @@ -73,7 +73,7 @@ class Midifile_reader { long read32bit(); int read16bit(); void msgenlarge(); - char *msg(); + unsigned char *msg(); int readheader(); void readtrack(); void sysex(); @@ -81,16 +81,17 @@ class Midifile_reader { int egetc(); int msgleng(); - int readmt(const char *, int); + int readmt(const char*,int); long to32bit(int,int,int,int); int to16bit(int,int); + void mferror(char *); void mferror(const char *); void badbyte(int); void metaevent(int); void msgadd(int); void chanmessage(int,int,int); - char *Msgbuff; + unsigned char *Msgbuff; long Msgsize; long Msgindex; }; diff --git a/plugins/MidiImport/portsmf/strparse.cpp b/plugins/MidiImport/portsmf/strparse.cpp index 7665b4ae058..592a21d62f4 100644 --- a/plugins/MidiImport/portsmf/strparse.cpp +++ b/plugins/MidiImport/portsmf/strparse.cpp @@ -1,5 +1,5 @@ -#include #include +#include // #include -- for debugging (cout) #include "ctype.h" using namespace std; @@ -48,10 +48,10 @@ void String_parse::get_nonspace_quoted(string &field) } -char *escape_chars[] = { (char *) "\\n", (char *)"\\t", (char *)"\\\\", (char *)"\\r", (char *) "\\\""}; +static const char *const escape_chars[] = {"\\n", "\\t", "\\\\", "\\r", "\\\""}; -void string_escape(string &result, char *str, const char *quote) +void string_escape(string &result, const char *str, const char *quote) { int length = (int) strlen(str); if (quote[0]) { @@ -59,8 +59,8 @@ void string_escape(string &result, char *str, const char *quote) } for (int i = 0; i < length; i++) { if (!isalnum((unsigned char) str[i])) { - char *chars = (char *)"\n\t\\\r\""; - char *special = strchr(chars, str[i]); + const char *const chars = "\n\t\\\r\""; + const char *const special = strchr(chars, str[i]); if (special) { result.append(escape_chars[special - chars]); } else { @@ -78,7 +78,7 @@ void String_parse::get_remainder(std::string &field) field.clear(); skip_space(); int len = str->length() - pos; - if ((*str)[len - 1] == '\n') { // if str ends in newline, + if ((len > 0) && ((*str)[len - 1] == '\n')) { // if str ends in newline, len--; // reduce length to ignore newline } field.insert(0, *str, pos, len); diff --git a/plugins/MidiImport/portsmf/strparse.h b/plugins/MidiImport/portsmf/strparse.h index 74f01591974..0c64b07b934 100644 --- a/plugins/MidiImport/portsmf/strparse.h +++ b/plugins/MidiImport/portsmf/strparse.h @@ -15,4 +15,4 @@ class String_parse { void get_remainder(std::string &field); }; -void string_escape(std::string &result, char *s, const char *quote); +void string_escape(std::string &result, const char *s, const char *quote); diff --git a/plugins/MidiImport/portsmf/trace.cpp b/plugins/MidiImport/portsmf/trace.cpp index 7c1999db570..38c050fe466 100644 --- a/plugins/MidiImport/portsmf/trace.cpp +++ b/plugins/MidiImport/portsmf/trace.cpp @@ -15,7 +15,7 @@ void trace(char *format, ...) char msg[256]; va_list args; va_start(args, format); - _vsnprintf(msg, 256, format, args); + _vsnprintf_s(msg, 256, _TRUNCATE, format, args); va_end(args); #ifdef _DEBUG _CrtDbgReport(_CRT_WARN, NULL, NULL, NULL, msg); diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index bff032deed9..eb6c51cba82 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -34,7 +34,7 @@ Plugin::Descriptor PLUGIN_EXPORT multitapecho_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Multitap Echo", - QT_TRANSLATE_NOOP( "pluginBrowser", "A multitap echo delay plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A multitap echo delay plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index 4dde05b0f3b..8ab635b4f92 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -68,7 +68,7 @@ Plugin::Descriptor PLUGIN_EXPORT opulenz_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "OpulenZ", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "2-operator FM Synth" ), "Raine M. Ekman ", 0x0100, diff --git a/plugins/ReverbSC/ReverbSC.cpp b/plugins/ReverbSC/ReverbSC.cpp index 3d56fd0d0ae..f54002199d5 100644 --- a/plugins/ReverbSC/ReverbSC.cpp +++ b/plugins/ReverbSC/ReverbSC.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT reverbsc_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "ReverbSC", - QT_TRANSLATE_NOOP( "pluginBrowser", "Reverb algorithm by Sean Costello" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Reverb algorithm by Sean Costello" ), "Paul Batchelor", 0x0123, Plugin::Effect, diff --git a/plugins/SpectrumAnalyzer/Analyzer.cpp b/plugins/SpectrumAnalyzer/Analyzer.cpp index 656d18bd4d6..87a8542e913 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.cpp +++ b/plugins/SpectrumAnalyzer/Analyzer.cpp @@ -42,7 +42,7 @@ extern "C" { { "spectrumanalyzer", "Spectrum Analyzer", - QT_TRANSLATE_NOOP("pluginBrowser", "A graphical spectrum analyzer."), + QT_TRANSLATE_NOOP("PluginBrowser", "A graphical spectrum analyzer."), "Martin Pavelek ", 0x0112, Plugin::Effect, diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp index f1aad2a01b5..ddd1489e85d 100644 --- a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp +++ b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp @@ -151,8 +151,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *freqRangeCombo = new ComboBox(this, tr("Frequency range")); freqRangeCombo->setToolTip(tr("Frequency range")); - freqRangeCombo->setMinimumSize(100, 22); - freqRangeCombo->setMaximumSize(200, 22); + freqRangeCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + freqRangeCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); freqRangeCombo->setModel(&controls->m_freqRangeModel); config_layout->addWidget(freqRangeCombo, 0, 3, 2, 1); @@ -171,8 +171,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *ampRangeCombo = new ComboBox(this, tr("Amplitude range")); ampRangeCombo->setToolTip(tr("Amplitude range")); - ampRangeCombo->setMinimumSize(100, 22); - ampRangeCombo->setMaximumSize(200, 22); + ampRangeCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + ampRangeCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); ampRangeCombo->setModel(&controls->m_ampRangeModel); config_layout->addWidget(ampRangeCombo, 2, 3, 2, 1); @@ -201,8 +201,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *windowCombo = new ComboBox(this, tr("FFT window type")); windowCombo->setToolTip(tr("FFT window type")); - windowCombo->setMinimumSize(100, 22); - windowCombo->setMaximumSize(200, 22); + windowCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + windowCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); windowCombo->setModel(&controls->m_windowModel); config_layout->addWidget(windowCombo, 2, 5, 2, 1); processor->rebuildWindow(); diff --git a/plugins/Vectorscope/Vectorscope.cpp b/plugins/Vectorscope/Vectorscope.cpp index f8bc30c40df..30d33aa2d8f 100644 --- a/plugins/Vectorscope/Vectorscope.cpp +++ b/plugins/Vectorscope/Vectorscope.cpp @@ -33,7 +33,7 @@ extern "C" { { STRINGIFY(PLUGIN_NAME), "Vectorscope", - QT_TRANSLATE_NOOP("pluginBrowser", "A stereo field visualizer."), + QT_TRANSLATE_NOOP("PluginBrowser", "A stereo field visualizer."), "Martin Pavelek ", 0x0100, Plugin::Effect, diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index 80f209a7b1c..fb805aed3c3 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT vsteffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "VST", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for using arbitrary VST effects inside LMMS." ), "Tobias Doerffel ", 0x0200, diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index ab740838855..e9133ff325e 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -374,7 +374,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * { sprintf( paramStr, "%d", i); m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)), - 0.0f, 1.0f, 0.01f, _eff, tr( paramStr ) ); + 0.0f, 1.0f, 0.01f, _eff, paramStr ); } FloatModel * model = m_vi->knobFModel[i]; diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 6e648edd481..e4eff06bdc8 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -53,7 +53,7 @@ extern "C" { Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { STRINGIFY( - PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("pluginBrowser", + PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("PluginBrowser", "Mathematical expression parser"), "Orr Dvori", 0x0100, Plugin::Instrument, new PluginPixmapLoader("logo"), NULL, NULL }; diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 51af71f55eb..da0f17a3c9e 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -55,7 +55,7 @@ Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "AudioFileProcessor", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Simple sampler with various settings for " "using samples (e.g. drums) in an " "instrument-track" ), @@ -541,7 +541,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, // interpolation selector m_interpBox = new ComboBox( this ); - m_interpBox->setGeometry( 142, 62, 82, 22 ); + m_interpBox->setGeometry( 142, 62, 82, ComboBox::DEFAULT_HEIGHT ); m_interpBox->setFont( pointSize<8>( m_interpBox->font() ) ); // wavegraph @@ -867,7 +867,7 @@ void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me ) void AudioFileProcessorWaveView::wheelEvent( QWheelEvent * _we ) { - zoom( _we->delta() > 0 ); + zoom( _we->angleDelta().y() > 0 ); update(); } diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index caa272fa7f0..3bc5785ef64 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitinvader_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "BitInvader", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Customizable wavetable synthesizer" ), "Andreas Brandmaier ", 0x0100, diff --git a/plugins/carlapatchbay/carlapatchbay.cpp b/plugins/carlapatchbay/carlapatchbay.cpp index ad0c1f6aef5..0090b3fcccf 100644 --- a/plugins/carlapatchbay/carlapatchbay.cpp +++ b/plugins/carlapatchbay/carlapatchbay.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlapatchbay_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Carla Patchbay", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Carla Patchbay Instrument" ), "falkTX ", CARLA_VERSION_HEX, diff --git a/plugins/carlarack/carlarack.cpp b/plugins/carlarack/carlarack.cpp index ee2a788354f..3b3f443abb4 100644 --- a/plugins/carlarack/carlarack.cpp +++ b/plugins/carlarack/carlarack.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlarack_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Carla Rack", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Carla Rack Instrument" ), "falkTX ", CARLA_VERSION_HEX, diff --git a/plugins/dynamics_processor/dynamics_processor.cpp b/plugins/dynamics_processor/dynamics_processor.cpp index 9bf7d9b3c90..38c74cf19de 100644 --- a/plugins/dynamics_processor/dynamics_processor.cpp +++ b/plugins/dynamics_processor/dynamics_processor.cpp @@ -38,7 +38,7 @@ Plugin::Descriptor PLUGIN_EXPORT dynamicsprocessor_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Dynamics Processor", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for processing dynamics in a flexible way" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index 5f36aae93f1..fe770c1ac1f 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -45,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT kicker_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Kicker", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Versatile drum synthesizer" ), "Tobias Doerffel ", 0x0100, diff --git a/plugins/ladspa_browser/ladspa_browser.cpp b/plugins/ladspa_browser/ladspa_browser.cpp index d265bc0e3e2..b363336346f 100644 --- a/plugins/ladspa_browser/ladspa_browser.cpp +++ b/plugins/ladspa_browser/ladspa_browser.cpp @@ -49,7 +49,7 @@ Plugin::Descriptor PLUGIN_EXPORT ladspabrowser_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LADSPA Plugin Browser", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "List installed LADSPA plugins" ), "Danny McRae ", 0x0100, diff --git a/plugins/ladspa_browser/ladspa_port_dialog.cpp b/plugins/ladspa_browser/ladspa_port_dialog.cpp index d7b124b3fa1..c213c6f0235 100644 --- a/plugins/ladspa_browser/ladspa_port_dialog.cpp +++ b/plugins/ladspa_browser/ladspa_port_dialog.cpp @@ -65,7 +65,7 @@ ladspaPortDialog::ladspaPortDialog( const ladspa_key_t & _key ) for( int col = 0; col < 7; ++col ) { QTableWidgetItem * item = new QTableWidgetItem; - item->setFlags( 0 ); + item->setFlags(QFlag(0)); settings->setItem( row, col, item ); } diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index ad3d33b0a04..cc671749db1 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -83,7 +83,7 @@ Plugin::Descriptor PLUGIN_EXPORT lb302_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LB302", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Incomplete monophonic imitation tb303" ), "Paul Giblock ", 0x0100, @@ -433,8 +433,11 @@ QString lb302Synth::nodeName() const // OBSOLETE. Break apart once we get Q_OBJECT to work. >:[ void lb302Synth::recalcFilter() { +#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) + vcf.loadRelaxed()->recalc(); +#else vcf.load()->recalc(); - +#endif // THIS IS OLD 3pole/24dB code, I may reintegrate it. Don't need it // right now. Should be toggled by LB_24_RES_TRICK at the moment. @@ -683,7 +686,11 @@ void lb302Synth::initNote( lb302Note *n) if(n->dead ==0){ // Swap next two blocks?? +#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) + vcf.loadRelaxed()->playNote(); +#else vcf.load()->playNote(); +#endif // Ensure envelope is recalculated vcf_envpos = ENVINC; diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index efb351fc839..8e0342377c3 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -45,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT monstro_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Monstro", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Monstrous 3-oscillator synth with modulation matrix" ), "Vesa Kivimäki ", 0x0100, @@ -1664,7 +1664,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc2VolKnob -> setVolumeKnob( true ); m_osc2WaveBox = new ComboBox( view ); - m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, 22 ); + m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc2WaveBox->setFont( pointSize<8>( m_osc2WaveBox->font() ) ); maketinyled( m_osc2SyncHButton, 212, O2ROW - 3, tr( "Hard sync oscillator 2" ) ) @@ -1679,18 +1679,18 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc3VolKnob -> setVolumeKnob( true ); m_osc3Wave1Box = new ComboBox( view ); - m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, 22 ); + m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); m_osc3Wave2Box = new ComboBox( view ); - m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, 22 ); + m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc3Wave2Box->setFont( pointSize<8>( m_osc3Wave2Box->font() ) ); maketinyled( m_osc3SyncHButton, 212, O3ROW - 3, tr( "Hard sync oscillator 3" ) ) maketinyled( m_osc3SyncRButton, 191, O3ROW - 3, tr( "Reverse sync oscillator 3" ) ) m_lfo1WaveBox = new ComboBox( view ); - m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, 22 ); + m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_lfo1WaveBox->setFont( pointSize<8>( m_lfo1WaveBox->font() ) ); maketsknob( m_lfo1AttKnob, LFOCOL1, LFOROW, tr( "Attack" ), " ms", "lfoKnob" ) @@ -1698,7 +1698,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) makeknob( m_lfo1PhsKnob, LFOCOL3, LFOROW, tr( "Phase" ), tr( " deg" ), "lfoKnob" ) m_lfo2WaveBox = new ComboBox( view ); - m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, 22 ); + m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_lfo2WaveBox->setFont( pointSize<8>( m_lfo2WaveBox->font() ) ); maketsknob( m_lfo2AttKnob, LFOCOL4, LFOROW, tr( "Attack" ), " ms", "lfoKnob" ) diff --git a/plugins/nes/Nes.cpp b/plugins/nes/Nes.cpp index d4bcc6881d1..d7c97fd160e 100644 --- a/plugins/nes/Nes.cpp +++ b/plugins/nes/Nes.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT nes_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Nescaline", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "A NES-like synthesizer" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index 6eb933afad3..c78d5c056b1 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT organic_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Organic", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Additive Synthesizer for organ-like sounds" ), "Andreas Brandmaier ", 0x0100, diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index e5170383438..d65850edcd0 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -54,7 +54,7 @@ Plugin::Descriptor PLUGIN_EXPORT patman_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "PatMan", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "GUS-compatible patch instrument" ), "Javier Serrano Polo ", 0x0100, diff --git a/plugins/peak_controller_effect/peak_controller_effect.cpp b/plugins/peak_controller_effect/peak_controller_effect.cpp index 9d1e6ccf465..01d24600e4e 100644 --- a/plugins/peak_controller_effect/peak_controller_effect.cpp +++ b/plugins/peak_controller_effect/peak_controller_effect.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT peakcontrollereffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Peak Controller", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for controlling knobs with sound peaks" ), "Paul Giblock ", 0x0100, diff --git a/plugins/sf2_player/patches_dialog.h b/plugins/sf2_player/patches_dialog.h index a2c88a79d1e..76387e830c4 100644 --- a/plugins/sf2_player/patches_dialog.h +++ b/plugins/sf2_player/patches_dialog.h @@ -43,7 +43,7 @@ class patchesDialog : public QDialog, private Ui::patchesDialog public: // Constructor. - patchesDialog(QWidget *pParent = 0, Qt::WindowFlags wflags = 0); + patchesDialog(QWidget *pParent = 0, Qt::WindowFlags wflags = QFlag(0)); // Destructor. virtual ~patchesDialog(); diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 7065a08002c..7a07b0ec697 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -57,7 +57,7 @@ Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Sf2 Player", - QT_TRANSLATE_NOOP( "pluginBrowser", "Player for SoundFont files" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Player for SoundFont files" ), "Paul Giblock ", 0x0100, Plugin::Instrument, diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index 1aacdc12c3b..08d418836c2 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -62,7 +62,7 @@ Plugin::Descriptor PLUGIN_EXPORT sfxr_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "sfxr", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "LMMS port of sfxr" ), "Wong Cho Ching", 0x0100, diff --git a/plugins/sid/sid_instrument.cpp b/plugins/sid/sid_instrument.cpp index 27f874e12dc..485fdfffb58 100644 --- a/plugins/sid/sid_instrument.cpp +++ b/plugins/sid/sid_instrument.cpp @@ -75,7 +75,7 @@ Plugin::Descriptor PLUGIN_EXPORT sid_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "SID", - QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of the MOS6581 and MOS8580 " + QT_TRANSLATE_NOOP( "PluginBrowser", "Emulation of the MOS6581 and MOS8580 " "SID.\nThis chip was used in the Commodore 64 computer." ), "Csaba Hruska " diff --git a/plugins/stereo_enhancer/stereo_enhancer.cpp b/plugins/stereo_enhancer/stereo_enhancer.cpp index 2faa5846d2f..3f5a9a38c29 100644 --- a/plugins/stereo_enhancer/stereo_enhancer.cpp +++ b/plugins/stereo_enhancer/stereo_enhancer.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereoenhancer_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "StereoEnhancer Effect", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for enhancing stereo separation of a stereo input file" ), "Lou Herard ", 0x0100, diff --git a/plugins/stereo_matrix/stereo_matrix.cpp b/plugins/stereo_matrix/stereo_matrix.cpp index a03a615ba37..2ec9b49509e 100644 --- a/plugins/stereo_matrix/stereo_matrix.cpp +++ b/plugins/stereo_matrix/stereo_matrix.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereomatrix_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Stereo Matrix", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for freely manipulating stereo output" ), "Paul Giblock ", 0x0100, diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index f9e2e7ede56..6f968985ced 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -50,7 +50,7 @@ Plugin::Descriptor PLUGIN_EXPORT malletsstk_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Mallets", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Tuneful things to bang on" ), "Danny McRae ", 0x0100, @@ -403,7 +403,7 @@ malletsInstrumentView::malletsInstrumentView( malletsInstrument * _instrument, changePreset(); // Show widget m_presetsCombo = new ComboBox( this, tr( "Instrument" ) ); - m_presetsCombo->setGeometry( 140, 50, 99, 22 ); + m_presetsCombo->setGeometry( 140, 50, 99, ComboBox::DEFAULT_HEIGHT ); m_presetsCombo->setFont( pointSize<8>( m_presetsCombo->font() ) ); connect( &_instrument->m_presetsModel, SIGNAL( dataChanged() ), diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 8093d218c04..9fdae48337f 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -49,7 +49,7 @@ Plugin::Descriptor PLUGIN_EXPORT tripleoscillator_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "TripleOscillator", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Three powerful oscillators you can modulate " "in several ways" ), "Tobias Doerffel ", diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index b17a1684541..dc1723db472 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -71,7 +71,7 @@ Plugin::Descriptor Q_DECL_EXPORT vestige_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "VeSTige", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "VST-host for using VST(i)-plugins within LMMS" ), "Tobias Doerffel ", 0x0100, @@ -991,7 +991,7 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume { sprintf( paramStr, "%d", i); m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)), - 0.0f, 1.0f, 0.01f, castModel(), tr( paramStr ) ); + 0.0f, 1.0f, 0.01f, castModel(), paramStr ); } FloatModel * model = m_vi->knobFModel[i]; diff --git a/plugins/vibed/vibed.cpp b/plugins/vibed/vibed.cpp index c663660826e..6610bab0e43 100644 --- a/plugins/vibed/vibed.cpp +++ b/plugins/vibed/vibed.cpp @@ -48,7 +48,7 @@ Plugin::Descriptor PLUGIN_EXPORT vibedstrings_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Vibed", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Vibrating string modeler" ), "Danny McRae ", 0x0100, diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 7d6a45940d2..278ab8b2d71 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -560,7 +560,7 @@ void VstPlugin::loadParameterDisplays() void VstPlugin::savePreset( ) { QString presName = currentProgramName().isEmpty() ? tr(": default") : currentProgramName(); - presName.replace(tr("\""), tr("'")); // QFileDialog unable to handle double quotes properly + presName.replace("\"", "'"); // QFileDialog unable to handle double quotes properly FileDialog sfd( NULL, tr( "Save Preset" ), presName.section(": ", 1, 1) + tr(".fxp"), tr( "Vst Plugin Preset (*.fxp *.fxb)" ) ); diff --git a/plugins/watsyn/Watsyn.cpp b/plugins/watsyn/Watsyn.cpp index a5af401ccf7..b60dd5a99c2 100644 --- a/plugins/watsyn/Watsyn.cpp +++ b/plugins/watsyn/Watsyn.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT watsyn_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Watsyn", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "4-oscillator modulatable wavetable synth" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/waveshaper/waveshaper.cpp b/plugins/waveshaper/waveshaper.cpp index a3bf2ddfb12..5327d931d9c 100644 --- a/plugins/waveshaper/waveshaper.cpp +++ b/plugins/waveshaper/waveshaper.cpp @@ -38,7 +38,7 @@ Plugin::Descriptor PLUGIN_EXPORT waveshaper_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Waveshaper Effect", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for waveshaping" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 429948e7508..04f7bda0ff3 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -58,7 +58,7 @@ Plugin::Descriptor PLUGIN_EXPORT zynaddsubfx_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "ZynAddSubFX", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Embedded ZynAddSubFX" ), "Tobias Doerffel ", 0x0100, diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index e459d0ceb65..2fd1cea125f 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -898,3 +898,8 @@ void AutomationPattern::generateTangents( timeMap::const_iterator it, it++; } } + + + + + diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 53262dac7df..5336654aef0 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -38,6 +38,11 @@ #include "lmmsversion.h" +// Vector with all the upgrade methods +const std::vector ConfigManager::UPGRADE_METHODS = { + &ConfigManager::upgrade_1_1_90 , &ConfigManager::upgrade_1_1_91 +}; + static inline QString ensureTrailingSlash(const QString & s ) { if(! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\')) @@ -51,7 +56,9 @@ static inline QString ensureTrailingSlash(const QString & s ) ConfigManager * ConfigManager::s_instanceOfMe = NULL; -ConfigManager::ConfigManager() : m_version(defaultVersion()) +ConfigManager::ConfigManager() : + m_version(defaultVersion()), + m_configVersion( UPGRADE_METHODS.size() ) { if (QFileInfo::exists(qApp->applicationDirPath() + PORTABLE_MODE_FILE)) { @@ -114,7 +121,7 @@ void ConfigManager::upgrade_1_1_90() void ConfigManager::upgrade_1_1_91() -{ +{ // rename displaydbv to displaydbfs if (!value("app", "displaydbv").isNull()) { setValue("app", "displaydbfs", value("app", "displaydbv")); @@ -131,17 +138,15 @@ void ConfigManager::upgrade() return; } - ProjectVersion createdWith = m_version; - - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.90") - { - upgrade_1_1_90(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_configVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.91") - { - upgrade_1_1_91(); - } + ProjectVersion createdWith = m_version; // Don't use old themes as they break the UI (i.e. 0.4 != 1.0, etc) if (createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION) @@ -151,6 +156,7 @@ void ConfigManager::upgrade() // Bump the version, now that we are upgraded m_version = LMMS_VERSION; + m_configVersion = UPGRADE_METHODS.size(); } QString ConfigManager::defaultVersion() const @@ -400,11 +406,23 @@ void ConfigManager::loadConfigFile(const QString & configFile) QDomNode node = root.firstChild(); - // Cache the config version for upgrade() + // Cache LMMS version if (!root.attribute("version").isNull()) { m_version = root.attribute("version"); } + // Get the version of the configuration file (for upgrade purposes) + if( root.attribute("configversion").isNull() ) + { + m_configVersion = legacyConfigVersion(); // No configversion attribute found + } + else + { + bool success; + m_configVersion = root.attribute("configversion").toUInt(&success); + if( !success ) qWarning("Config Version conversion failure."); + } + // create the settings-map out of the DOM while(!node.isNull()) { @@ -565,6 +583,7 @@ void ConfigManager::saveConfigFile() QDomElement lmms_config = doc.createElement("lmms"); lmms_config.setAttribute("version", m_version); + lmms_config.setAttribute("configversion", m_configVersion); doc.appendChild(lmms_config); for(settingsMap::iterator it = m_settings.begin(); @@ -673,3 +692,25 @@ void ConfigManager::initDevelopmentWorkingDir() cmakeCache.close(); } } + +// If configversion is not present, we will convert the LMMS version to the appropriate +// configuration file version for backwards compatibility. +unsigned int ConfigManager::legacyConfigVersion() +{ + ProjectVersion createdWith = m_version; + + createdWith.setCompareType(ProjectVersion::Build); + + if( createdWith < "1.1.90" ) + { + return 0; + } + else if( createdWith < "1.1.91" ) + { + return 1; + } + else + { + return 2; + } +} diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index a4efdcccdc2..326f01533e7 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -49,7 +49,28 @@ static void findIds(const QDomElement& elem, QList& idList); - +// Vector with all the upgrade methods +const std::vector DataFile::UPGRADE_METHODS = { + &DataFile::upgrade_0_2_1_20070501 , &DataFile::upgrade_0_2_1_20070508, + &DataFile::upgrade_0_3_0_rc2 , &DataFile::upgrade_0_3_0, + &DataFile::upgrade_0_4_0_20080104 , &DataFile::upgrade_0_4_0_20080118, + &DataFile::upgrade_0_4_0_20080129 , &DataFile::upgrade_0_4_0_20080409, + &DataFile::upgrade_0_4_0_20080607 , &DataFile::upgrade_0_4_0_20080622, + &DataFile::upgrade_0_4_0_beta1 , &DataFile::upgrade_0_4_0_rc2, + &DataFile::upgrade_1_0_99 , &DataFile::upgrade_1_1_0, + &DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3, + &DataFile::upgrade_1_3_0 , &DataFile::upgrade_noHiddenClipNames +}; + +// Vector of all versions that have upgrade routines. +const std::vector DataFile::UPGRADE_VERSIONS = { + "0.2.1-20070501" , "0.2.1-20070508" , "0.3.0-rc2", + "0.3.0" , "0.4.0-20080104" , "0.4.0-20080118", + "0.4.0-20080129" , "0.4.0-20080409" , "0.4.0-20080607", + "0.4.0-20080622" , "0.4.0-beta1" , "0.4.0-rc2", + "1.0.99-0" , "1.1.0-0" , "1.1.91-0", + "1.2.0-rc3" , "1.3.0" +}; DataFile::typeDescStruct DataFile::s_types[DataFile::TypeCount] = @@ -71,11 +92,12 @@ DataFile::DataFile( Type type ) : QDomDocument( "lmms-project" ), m_content(), m_head(), - m_type( type ) + m_type( type ), + m_fileVersion( UPGRADE_METHODS.size() ) { appendChild( createProcessingInstruction("xml", "version=\"1.0\"")); QDomElement root = createElement( "lmms-project" ); - root.setAttribute( "version", LDF_VERSION_STRING ); + root.setAttribute( "version", m_fileVersion ); root.setAttribute( "type", typeName( type ) ); root.setAttribute( "creator", "LMMS" ); root.setAttribute( "creatorversion", LMMS_VERSION ); @@ -95,7 +117,8 @@ DataFile::DataFile( Type type ) : DataFile::DataFile( const QString & _fileName ) : QDomDocument(), m_content(), - m_head() + m_head(), + m_fileVersion( UPGRADE_METHODS.size() ) { QFile inFile( _fileName ); if( !inFile.open( QIODevice::ReadOnly ) ) @@ -873,31 +896,6 @@ void DataFile::upgrade_1_1_91() } -void DataFile::upgrade_1_2_0_rc3() -{ - // Upgrade from earlier bbtrack beat note behaviour of adding - // steps if a note is placed after the last step. - QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); - for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) - { - QDomNodeList patterns = bbtracks.item( i - ).toElement().elementsByTagName( - "pattern" ); - for( int j = 0; !patterns.item( j ).isNull(); ++j ) - { - int patternLength, steps; - QDomElement el = patterns.item( j ).toElement(); - if( el.attribute( "len" ) != "" ) - { - patternLength = el.attribute( "len" ).toInt(); - steps = patternLength / 12; - el.setAttribute( "steps", steps ); - } - } - } -} - - static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) { if( el.hasAttribute( "syncmode" ) ) @@ -930,8 +928,30 @@ static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) } -void DataFile::upgrade_1_2_0_rc2_42() +void DataFile::upgrade_1_2_0_rc3() { + // Upgrade from earlier bbtrack beat note behaviour of adding + // steps if a note is placed after the last step. + QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); + for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) + { + QDomNodeList patterns = bbtracks.item( i + ).toElement().elementsByTagName( + "pattern" ); + for( int j = 0; !patterns.item( j ).isNull(); ++j ) + { + int patternLength, steps; + QDomElement el = patterns.item( j ).toElement(); + if( el.attribute( "len" ) != "" ) + { + patternLength = el.attribute( "len" ).toInt(); + steps = patternLength / 12; + el.setAttribute( "steps", steps ); + } + } + } + + // DataFile::upgrade_1_2_0_rc2_42 QDomElement el = firstChildElement(); while ( !el.isNull() ) { @@ -1335,7 +1355,7 @@ void DataFile::upgrade_1_3_0() } } -void DataFile::upgrade_1_3_0_alpha_1() +void DataFile::upgrade_noHiddenClipNames() { QDomNodeList tracks = elementsByTagName("track"); @@ -1367,93 +1387,19 @@ void DataFile::upgrade_1_3_0_alpha_1() void DataFile::upgrade() { - ProjectVersion version = - documentElement().attribute( "creatorversion" ). - replace( "svn", "" ); - - if( version < "0.2.1-20070501" ) - { - upgrade_0_2_1_20070501(); - } - - if( version < "0.2.1-20070508" ) - { - upgrade_0_2_1_20070508(); - } - - if( version < "0.3.0-rc2" ) - { - upgrade_0_3_0_rc2(); - } - - if( version < "0.3.0" ) - { - upgrade_0_3_0(); - } - - if( version < "0.4.0-20080104" ) - { - upgrade_0_4_0_20080104(); - } - - if( version < "0.4.0-20080118" ) - { - upgrade_0_4_0_20080118(); - } - - if( version < "0.4.0-20080129" ) - { - upgrade_0_4_0_20080129(); - } - - if( version < "0.4.0-20080409" ) - { - upgrade_0_4_0_20080409(); - } - - if( version < "0.4.0-20080607" ) - { - upgrade_0_4_0_20080607(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_fileVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if( version < "0.4.0-20080622" ) - { - upgrade_0_4_0_20080622(); - } - - if( version < "0.4.0-beta1" ) - { - upgrade_0_4_0_beta1(); - } - if( version < "0.4.0-rc2" ) - { - upgrade_0_4_0_rc2(); - } - if( version < "1.0.99-0" ) - { - upgrade_1_0_99(); - } - if( version < "1.1.0-0" ) - { - upgrade_1_1_0(); - } - if( version < "1.1.91-0" ) - { - upgrade_1_1_91(); - } - if( version < "1.2.0-rc3" ) - { - upgrade_1_2_0_rc3(); - upgrade_1_2_0_rc2_42(); - } - if (version < "1.3.0-alpha-1"){ upgrade_1_3_0_alpha_1(); } - if( version < "1.3.0" ) - { - upgrade_1_3_0(); - } + // Bump the file version (which should be the size of the upgrade methods vector) + m_fileVersion = UPGRADE_METHODS.size(); // update document meta data - documentElement().setAttribute( "version", LDF_VERSION_STRING ); + documentElement().setAttribute( "version", m_fileVersion ); documentElement().setAttribute( "type", typeName( type() ) ); documentElement().setAttribute( "creator", "LMMS" ); documentElement().setAttribute( "creatorversion", LMMS_VERSION ); @@ -1513,18 +1459,31 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) m_type = type( root.attribute( "type" ) ); m_head = root.elementsByTagName( "head" ).item( 0 ).toElement(); + if( root.hasAttribute( "version" ) ) + { + if( root.attribute( "version" ) == "1.0" ){ + // The file versioning is now a unsigned int, not maj.min, so we use + // legacyFileVersion() to retrieve the appropriate version + m_fileVersion = legacyFileVersion(); + } + else + { + bool success; + m_fileVersion = root.attribute( "version" ).toUInt( &success ); + if( !success ) qWarning("File Version conversion failure."); + } + } if( root.hasAttribute( "creatorversion" ) ) { - // compareType defaults to Build,so it doesn't have to be set here + // compareType defaults to All, so it doesn't have to be set here ProjectVersion createdWith = root.attribute( "creatorversion" ); ProjectVersion openedWith = LMMS_VERSION; if ( createdWith != openedWith ) { - // only one compareType needs to be set, and we can compare on one line because setCompareType returns ProjectVersion - if( createdWith.setCompareType( ProjectVersion::Minor ) - != openedWith ) + if( createdWith.setCompareType( ProjectVersion::Minor ) != + openedWith.setCompareType( ProjectVersion::Minor ) ) { if( gui != nullptr && root.attribute( "type" ) == "song" ) { @@ -1546,11 +1505,9 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) } // the upgrade needs to happen after the warning as it updates the project version. - if( createdWith.setCompareType( ProjectVersion::Build ) - < openedWith ) - { - upgrade(); - } + if( createdWith.setCompareType( ProjectVersion::All ) < + openedWith.setCompareType( ProjectVersion::All ) ) + { upgrade(); } } } @@ -1572,3 +1529,17 @@ void findIds(const QDomElement& elem, QList& idList) child = child.nextSiblingElement(); } } + +unsigned int DataFile::legacyFileVersion() +{ + // Version of LMMs that created this project + ProjectVersion creator = + documentElement().attribute( "creatorversion" ). + replace( "svn", "" ); + + // Get an iterator pointing at the first upgrade we need to run (or at the end if there is no such upgrade) + auto firstRequiredUpgrade = std::upper_bound( UPGRADE_VERSIONS.begin(), UPGRADE_VERSIONS.end(), creator ); + + // Convert the iterator to an index, which is our file version (starting at 0) + return std::distance( UPGRADE_VERSIONS.begin(), firstRequiredUpgrade ); +} diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 46026b96f8c..2c221cdcc01 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -43,16 +43,14 @@ const float RES_PRECISION = 1000.0f; // names for env- and lfo-targets - first is name being displayed to user // and second one is used internally, e.g. for saving/restoring settings -const QString InstrumentSoundShaping::targetNames[InstrumentSoundShaping::NumTargets][3] = +const char *const InstrumentSoundShaping::targetNames[InstrumentSoundShaping::NumTargets][3] = { - { InstrumentSoundShaping::tr( "VOLUME" ), "vol", - InstrumentSoundShaping::tr( "Volume" ) }, -/* InstrumentSoundShaping::tr( "Pan" ), - InstrumentSoundShaping::tr( "Pitch" ),*/ - { InstrumentSoundShaping::tr( "CUTOFF" ), "cut", - InstrumentSoundShaping::tr( "Cutoff frequency" ) }, - { InstrumentSoundShaping::tr( "RESO" ), "res", - InstrumentSoundShaping::tr( "Resonance" ) } + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "VOLUME"), "vol", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Volume") }, + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "CUTOFF"), "cut", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Cutoff frequency") }, + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "RESO"), "res", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Resonance") } } ; @@ -77,7 +75,7 @@ InstrumentSoundShaping::InstrumentSoundShaping( value_for_zero_amount, this ); m_envLfoParameters[i]->setDisplayName( - tr( targetNames[i][2].toUtf8().constData() ) ); + tr( targetNames[i][2] ) ); } m_filterModel.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index febbe5a9192..86acb661be5 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -59,6 +59,8 @@ LadspaManager::LadspaManager() for( QStringList::iterator it = ladspaDirectories.begin(); it != ladspaDirectories.end(); ++it ) { + // Skip empty entries as QDir will interpret it as the working directory + if ((*it).isEmpty()) { continue; } QDir directory( ( *it ) ); QFileInfoList list = directory.entryInfoList(); for( QFileInfoList::iterator file = list.begin(); diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index b7dc5bff535..ecd1d6ea791 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -316,8 +316,7 @@ inline void Oscillator::recalcPhase() m_phaseOffset = m_ext_phaseOffset; m_phase += m_phaseOffset; } - m_phase = absFraction( m_phase )+2; // make sure we're not running - // negative when doing PM + m_phase = absFraction( m_phase ); } diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index 411f6fe5899..f1256654970 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -44,7 +44,7 @@ static Plugin::Descriptor dummyPluginDescriptor = { "dummy", "dummy", - QT_TRANSLATE_NOOP( "pluginBrowser", "no description" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "no description" ), "Tobias Doerffel ", 0x0100, Plugin::Undefined, diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index abf6421229e..16f86a17a4e 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -144,7 +144,12 @@ void PluginFactory::discoverPlugins() QSet files; for (const QString& searchPath : QDir::searchPaths("plugins")) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) + auto discoveredPluginList = QDir(searchPath).entryInfoList(nameFilters); + files.unite(QSet(discoveredPluginList.begin(), discoveredPluginList.end())); +#else files.unite(QDir(searchPath).entryInfoList(nameFilters).toSet()); +#endif } // Cheap dependency handling: zynaddsubfx needs ZynAddSubFxCore. By loading diff --git a/src/core/ProjectVersion.cpp b/src/core/ProjectVersion.cpp index ea97a6fec7d..6f3dcddbeb1 100644 --- a/src/core/ProjectVersion.cpp +++ b/src/core/ProjectVersion.cpp @@ -27,123 +27,105 @@ #include "ProjectVersion.h" -int parseMajor(QString & version) { - return version.section( '.', 0, 0 ).toInt(); -} - - - - -int parseMinor(QString & version) { - return version.section( '.', 1, 1 ).toInt(); -} - - - - -int parseRelease(QString & version) { - return version.section( '.', 2, 2 ).section( '-', 0, 0 ).toInt(); -} - - - - -QString parseStage(QString & version) { - return version.section( '.', 2, 2 ).section( '-', 1 ); -} - - - - -int parseBuild(QString & version) { - return version.section( '.', 3 ).toInt(); -} - ProjectVersion::ProjectVersion(QString version, CompareType c) : m_version(version), - m_major(parseMajor(m_version)), - m_minor(parseMinor(m_version)), - m_release(parseRelease(m_version)), - m_stage(parseStage(m_version)), - m_build(parseBuild(m_version)), m_compareType(c) { + // Version numbers may have build data, prefixed with a '+', + // but this mustn't affect version precedence in comparisons + QString metadataStripped = version.split("+").first(); + // They must have an obligatory initial segement, and may have + // optional identifiers prefaced by a '-'. Both parts affect precedence + QString obligatorySegment = metadataStripped.section('-', 0, 0); + QString prereleaseSegment = metadataStripped.section('-', 1); + + // The obligatory segment consists of three identifiers: MAJOR.MINOR.PATCH + QStringList mainVersion = obligatorySegment.split("."); + // HACK: Pad invalid versions in order to prevent crashes + while (mainVersion.size() < 3){ mainVersion.append("0"); } + m_major = mainVersion.at(0).toInt(); + m_minor = mainVersion.at(1).toInt(); + m_patch = mainVersion.at(2).toInt(); + + // Any # of optional pre-release identifiers may follow, separated by '.'s + if (!prereleaseSegment.isEmpty()){ m_labels = prereleaseSegment.split("."); } + + // HACK: Handle old (1.2.2 and earlier), non-standard versions of the form + // MAJOR.MINOR.PATCH.COMMITS, used for non-release builds from source. + if (mainVersion.size() >= 4 && m_major <= 1 && m_minor <= 2 && m_patch <= 2){ + // Drop the standard version identifiers. erase(a, b) removes [a,b) + mainVersion.erase(mainVersion.begin(), mainVersion.begin() + 3); + // Prepend the remaining identifiers as prerelease versions + m_labels = mainVersion + m_labels; + // Bump the patch version. x.y.z-a < x.y.z, but we want x.y.z.a > x.y.z + m_patch += 1; + } } -ProjectVersion::ProjectVersion(const char* version, CompareType c) : - m_version(QString(version)), - m_major(parseMajor(m_version)), - m_minor(parseMinor(m_version)), - m_release(parseRelease(m_version)), - m_stage(parseStage(m_version)), - m_build(parseBuild(m_version)), - m_compareType(c) +ProjectVersion::ProjectVersion(const char* version, CompareType c) : ProjectVersion(QString(version), c) { } +//! @param c Determines the number of identifiers to check when comparing int ProjectVersion::compare(const ProjectVersion & a, const ProjectVersion & b, CompareType c) { - if(a.getMajor() != b.getMajor()) - { - return a.getMajor() - b.getMajor(); - } - if(c == Major) - { - return 0; - } - - if(a.getMinor() != b.getMinor()) - { - return a.getMinor() - b.getMinor(); - } - if(c == Minor) - { - return 0; + // How many identifiers to compare before we consider the versions equal + const int limit = static_cast(c); + + // Use the value of limit to zero out identifiers we don't care about + int aMaj = 0, bMaj = 0, aMin = 0, bMin = 0, aPat = 0, bPat = 0; + if (limit >= 1){ aMaj = a.getMajor(); bMaj = b.getMajor(); } + if (limit >= 2){ aMin = a.getMinor(); bMin = b.getMinor(); } + if (limit >= 3){ aPat = a.getPatch(); bPat = b.getPatch(); } + + // Then we can compare as if we care about every identifier + if(aMaj != bMaj){ return aMaj - bMaj; } + if(aMin != bMin){ return aMin - bMin; } + if(aPat != bPat){ return aPat - bPat; } + + // Decide how many optional identifiers we care about + const int maxLabels = qMax(0, limit - 3); + const auto aLabels = a.getLabels().mid(0, maxLabels); + const auto bLabels = b.getLabels().mid(0, maxLabels); + + // We can only compare identifiers if both versions have them + const int commonLabels = qMin(aLabels.size(), bLabels.size()); + // If one version has optional labels and the other doesn't, + // the one without them is bigger + if (commonLabels == 0){ return bLabels.size() - aLabels.size(); } + + // Otherwise, compare as many labels as we can + for (int i = 0; i < commonLabels; i++){ + const QString& labelA = aLabels.at(i); + const QString& labelB = bLabels.at(i); + // If both labels are the same, skip + if (labelA == labelB){ continue; } + // Numeric and non-numeric identifiers compare differently + bool aIsNumeric = false, bIsNumeric = false; + const int numA = labelA.toInt(&aIsNumeric); + const int numB = labelB.toInt(&bIsNumeric); + // toInt reads '-x' as a negative number, semver says it's non-numeric + aIsNumeric &= !labelA.startsWith("-"); + bIsNumeric &= !labelB.startsWith("-"); + // If only one identifier is numeric, that one is smaller + if (aIsNumeric != bIsNumeric){ return aIsNumeric ? -1 : 1; } + // If both are numeric, compare as numbers + if (aIsNumeric && bIsNumeric){ return numA - numB; } + // Otherwise, compare lexically + return labelA.compare(labelB); } - if(a.getRelease() != b.getRelease()) - { - return a.getRelease() - b.getRelease(); - } - if(c == Release) - { - return 0; - } - - if(!(a.getStage().isEmpty() && b.getStage().isEmpty())) - { - // make sure 0.x.y > 0.x.y-alpha - if(a.getStage().isEmpty()) - { - return 1; - } - if(b.getStage().isEmpty()) - { - return -1; - } - - // 0.x.y-beta > 0.x.y-alpha - int cmp = QString::compare(a.getStage(), b.getStage()); - if(cmp) - { - return cmp; - } - } - if(c == Stage) - { - return 0; - } - - return a.getBuild() - b.getBuild(); + // If everything else matches, the version with more labels is bigger + return aLabels.size() - bLabels.size(); } @@ -153,6 +135,3 @@ int ProjectVersion::compare(ProjectVersion v1, ProjectVersion v2) { return compare(v1, v2, std::min(v1.getCompareType(), v2.getCompareType())); } - - - diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 5f63e6ee859..07f28821bfc 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1203,7 +1203,11 @@ void Song::loadProject( const QString & fileName ) } else { +#if (QT_VERSION >= QT_VERSION_CHECK(5,15,0)) + QTextStream(stderr) << Engine::getSong()->errorSummary() << Qt::endl; +#else QTextStream(stderr) << Engine::getSong()->errorSummary() << endl; +#endif } } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index fe4941ef425..c7f6d62b0b5 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1138,26 +1138,26 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) { contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( individualTCO - ? "Delete (middle mousebutton)" - : "Delete selection (middle mousebutton)" ), + individualTCO + ? tr("Delete (middle mousebutton)") + : tr("Delete selection (middle mousebutton)"), [this](){ contextMenuAction( Remove ); } ); contextMenu.addSeparator(); contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( individualTCO - ? "Cut" - : "Cut selection" ), + individualTCO + ? tr("Cut") + : tr("Cut selection"), [this](){ contextMenuAction( Cut ); } ); } contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( individualTCO - ? "Copy" - : "Copy selection" ), + individualTCO + ? tr("Copy") + : tr("Copy selection"), [this](){ contextMenuAction( Copy ); } ); contextMenu.addAction( @@ -1169,9 +1169,9 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( individualTCO - ? "Mute/unmute (<%1> + middle click)" - : "Mute/unmute selection (<%1> + middle click)" ).arg(UI_CTRL_KEY), + (individualTCO + ? tr("Mute/unmute (<%1> + middle click)") + : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), [this](){ contextMenuAction( Mute ); } ); constructContextMenu( &contextMenu ); diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index 86f265b1270..ce506f2e17c 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -30,7 +30,9 @@ #ifdef LMMS_HAVE_OGGVORBIS - +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) +#include +#endif #include #include @@ -136,8 +138,13 @@ bool AudioFileOgg::startEncoding() // We give our ogg file a random serial number and avoid // 0 and UINT32_MAX which can get you into trouble. - qsrand( time( 0 ) ); +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) + QRandomGenerator::global()->seed(time(0)); + m_serialNo = 0xD0000000 + QRandomGenerator::global()->generate() % 0x0FFFFFFF; +#else + qsrand(time(0)); m_serialNo = 0xD0000000 + qrand() % 0x0FFFFFFF; +#endif ogg_stream_init( &m_os, m_serialNo ); // Now, build the three header packets and send through to the stream diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index ad67277ab35..865eeca8d5a 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -410,14 +410,14 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioPortAudio::name(), _parent ) { m_backend = new ComboBox( this, "BACKEND" ); - m_backend->setGeometry( 64, 15, 260, 20 ); + m_backend->setGeometry( 64, 15, 260, ComboBox::DEFAULT_HEIGHT ); QLabel * backend_lbl = new QLabel( tr( "Backend" ), this ); backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) ); backend_lbl->move( 8, 18 ); m_device = new ComboBox( this, "DEVICE" ); - m_device->setGeometry( 64, 35, 260, 20 ); + m_device->setGeometry( 64, 35, 260, ComboBox::DEFAULT_HEIGHT ); QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 86235f145b2..ceb9b17123e 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -483,6 +483,8 @@ struct ConnectPortVisitor : public Lv2Ports::Visitor ConnectPortVisitor::~ConnectPortVisitor() {} +// !This function must be realtime safe! +// use createPort to create any port before connecting void Lv2Proc::connectPort(std::size_t num) { ConnectPortVisitor connect; diff --git a/src/core/main.cpp b/src/core/main.cpp index 897d07abf54..c83578a3d10 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -40,6 +40,7 @@ #include #ifdef LMMS_BUILD_WIN32 +#define NOMINMAX #include #endif @@ -437,7 +438,7 @@ int main( int argc, char * * argv ) { return noInputFileError(); } - + QFile f( QString::fromLocal8Bit( argv[i] ) ); f.open( QIODevice::ReadOnly ); QByteArray d = qCompress( f.readAll() ) ; diff --git a/src/gui/AudioDeviceSetupWidget.cpp b/src/gui/AudioDeviceSetupWidget.cpp index fbec38c7682..feba3f1b0fb 100644 --- a/src/gui/AudioDeviceSetupWidget.cpp +++ b/src/gui/AudioDeviceSetupWidget.cpp @@ -26,7 +26,7 @@ AudioDeviceSetupWidget::AudioDeviceSetupWidget(const QString & caption, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(TabWidget::tr(caption.toLatin1())), parent) + TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent) { } diff --git a/src/gui/ControllerConnectionDialog.cpp b/src/gui/ControllerConnectionDialog.cpp index f0d3d10e9c2..6b6527b3342 100644 --- a/src/gui/ControllerConnectionDialog.cpp +++ b/src/gui/ControllerConnectionDialog.cpp @@ -187,7 +187,7 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, this, SLOT( userToggled() ) ); m_userController = new ComboBox( m_userGroupBox, "Controller" ); - m_userController->setGeometry( 10, 24, 200, 22 ); + m_userController->setGeometry( 10, 24, 200, ComboBox::DEFAULT_HEIGHT ); for (Controller * c : Engine::getSong()->controllers()) { m_userController->model()->addItem( c->name() ); diff --git a/src/gui/EffectSelectDialog.cpp b/src/gui/EffectSelectDialog.cpp index 79e40427b28..4423ea707d7 100644 --- a/src/gui/EffectSelectDialog.cpp +++ b/src/gui/EffectSelectDialog.cpp @@ -234,7 +234,7 @@ void EffectSelectDialog::rowChanged( const QModelIndex & _idx, { QLabel *label = new QLabel(m_descriptionWidget); QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; - labelText += "

" + tr("Description") + ": " + qApp->translate( "pluginBrowser", descriptor.description ) + "

"; + labelText += "

" + tr("Description") + ": " + qApp->translate( "PluginBrowser", descriptor.description ) + "

"; labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; label->setText(labelText); diff --git a/src/gui/MidiSetupWidget.cpp b/src/gui/MidiSetupWidget.cpp index 0c34544d6bf..8342070571b 100644 --- a/src/gui/MidiSetupWidget.cpp +++ b/src/gui/MidiSetupWidget.cpp @@ -31,7 +31,7 @@ MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & configSection, const QString & devName, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toLatin1())), parent), + TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent), m_configSection(configSection), m_device(nullptr) { diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index 671b58381ea..7fd1a6301a0 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -205,7 +205,9 @@ PluginDescWidget::PluginDescWidget(const PluginKey &_pk, setFixedHeight( DEFAULT_HEIGHT ); setMouseTracking( true ); setCursor( Qt::PointingHandCursor ); - setToolTip(_pk.description()); + setToolTip(_pk.desc->subPluginFeatures + ? _pk.description() + : tr(_pk.desc->description)); } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index e542039c50e..86ba61eb108 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -31,12 +31,14 @@ #include #include +#include "AudioDeviceSetupWidget.h" #include "debug.h" #include "embed.h" #include "Engine.h" #include "FileDialog.h" #include "gui_templates.h" #include "MainWindow.h" +#include "MidiSetupWidget.h" #include "Mixer.h" #include "ProjectJournal.h" #include "SetupDialog.h" @@ -195,14 +197,14 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : auto addLedCheckBox = [&XDelta, &YDelta, this]( - const char* ledText, + const QString &ledText, TabWidget* tw, int& counter, bool initialState, const char* toggledSlot, bool showRestartWarning ){ - LedCheckBox * checkBox = new LedCheckBox(tr(ledText), tw); + LedCheckBox * checkBox = new LedCheckBox(ledText, tw); counter++; checkBox->move(XDelta, YDelta * counter); checkBox->setChecked(initialState); @@ -221,21 +223,21 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : tr("Graphical user interface (GUI)"), general_w); - addLedCheckBox("Display volume as dBFS ", gui_tw, counter, + addLedCheckBox(tr("Display volume as dBFS "), gui_tw, counter, m_displaydBFS, SLOT(toggleDisplaydBFS(bool)), true); - addLedCheckBox("Enable tooltips", gui_tw, counter, + addLedCheckBox(tr("Enable tooltips"), gui_tw, counter, m_tooltips, SLOT(toggleTooltips(bool)), true); - addLedCheckBox("Enable master oscilloscope by default", gui_tw, counter, + addLedCheckBox(tr("Enable master oscilloscope by default"), gui_tw, counter, m_displayWaveform, SLOT(toggleDisplayWaveform(bool)), true); - addLedCheckBox("Enable all note labels in piano roll", gui_tw, counter, + addLedCheckBox(tr("Enable all note labels in piano roll"), gui_tw, counter, m_printNoteLabels, SLOT(toggleNoteLabels(bool)), false); - addLedCheckBox("Enable compact track buttons", gui_tw, counter, + addLedCheckBox(tr("Enable compact track buttons"), gui_tw, counter, m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool)), true); - addLedCheckBox("Enable one instrument-track-window mode", gui_tw, counter, + addLedCheckBox(tr("Enable one instrument-track-window mode"), gui_tw, counter, m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true); - addLedCheckBox("Show sidebar on the right-hand side", gui_tw, counter, + addLedCheckBox(tr("Show sidebar on the right-hand side"), gui_tw, counter, m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true); - addLedCheckBox("Mute automation tracks during solo", gui_tw, counter, + addLedCheckBox(tr("Mute automation tracks during solo"), gui_tw, counter, m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false); gui_tw->setFixedHeight(YDelta + YDelta * counter); @@ -248,11 +250,11 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : tr("Projects"), general_w); - addLedCheckBox("Compress project files by default", projects_tw, counter, + addLedCheckBox(tr("Compress project files by default"), projects_tw, counter, m_MMPZ, SLOT(toggleMMPZ(bool)), true); - addLedCheckBox("Create a backup file when saving a project", projects_tw, counter, + addLedCheckBox(tr("Create a backup file when saving a project"), projects_tw, counter, m_disableBackup, SLOT(toggleDisableBackup(bool)), false); - addLedCheckBox("Reopen last project on startup", projects_tw, counter, + addLedCheckBox(tr("Reopen last project on startup"), projects_tw, counter, m_openLastProject, SLOT(toggleOpenLastProject(bool)), false); projects_tw->setFixedHeight(YDelta + YDelta * counter); @@ -372,9 +374,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : TabWidget * ui_fx_tw = new TabWidget( tr("User interface (UI) effects vs. performance"), performance_w); - addLedCheckBox("Smooth scroll in song editor", ui_fx_tw, counter, + addLedCheckBox(tr("Smooth scroll in song editor"), ui_fx_tw, counter, m_smoothScroll, SLOT(toggleSmoothScroll(bool)), false); - addLedCheckBox("Display playback cursor in AudioFileProcessor", ui_fx_tw, counter, + addLedCheckBox(tr("Display playback cursor in AudioFileProcessor"), ui_fx_tw, counter, m_animateAFP, SLOT(toggleAnimateAFP(bool)), false); ui_fx_tw->setFixedHeight(YDelta + YDelta * counter); @@ -421,10 +423,10 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : connect(m_vstAlwaysOnTopCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleVSTAlwaysOnTop(bool))); - addLedCheckBox("Sync VST plugins to host playback", plugins_tw, counter, + addLedCheckBox(tr("Sync VST plugins to host playback"), plugins_tw, counter, m_syncVSTPlugins, SLOT(toggleSyncVSTPlugins(bool)), false); - addLedCheckBox("Keep effects running even without input", plugins_tw, counter, + addLedCheckBox(tr("Keep effects running even without input"), plugins_tw, counter, m_disableAutoQuit, SLOT(toggleDisableAutoQuit(bool)), false); plugins_tw->setFixedHeight(YDelta + YDelta * counter); @@ -511,7 +513,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_audioIfaceSetupWidgets.end(); ++it) { m_audioIfaceNames[ - tr(it.key().toLatin1())] = it.key(); + AudioDeviceSetupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_audioIfaceNames.begin(); it != m_audioIfaceNames.end(); ++it) @@ -657,7 +659,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_midiIfaceSetupWidgets.end(); ++it) { m_midiIfaceNames[ - tr(it.key().toLatin1())] = it.key(); + MidiSetupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_midiIfaceNames.begin(); it != m_midiIfaceNames.end(); ++it) @@ -736,14 +738,14 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : QVBoxLayout * pathSelectorsLayout = new QVBoxLayout; pathSelectorsLayout->setSpacing(10); - auto addPathEntry = [&](const char* caption, + auto addPathEntry = [&](const QString &caption, const QString& content, const char* setSlot, const char* openSlot, QLineEdit*& lineEdit, const char* pixmap = "project_open") { - TabWidget * newTw = new TabWidget(tr(caption), + TabWidget * newTw = new TabWidget(caption, pathSelectors); newTw->setFixedHeight(48); @@ -763,37 +765,37 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : pathSelectorsLayout->addSpacing(10); }; - addPathEntry("LMMS working directory", m_workingDir, + addPathEntry(tr("LMMS working directory"), m_workingDir, SLOT(setWorkingDir(const QString &)), SLOT(openWorkingDir()), m_workingDirLineEdit); - addPathEntry("VST plugins directory", m_vstDir, + addPathEntry(tr("VST plugins directory"), m_vstDir, SLOT(setVSTDir(const QString &)), SLOT(openVSTDir()), m_vstDirLineEdit); - addPathEntry("LADSPA plugins directories", m_ladspaDir, + addPathEntry(tr("LADSPA plugins directories"), m_ladspaDir, SLOT(setLADSPADir(const QString &)), SLOT(openLADSPADir()), m_ladspaDirLineEdit, "add_folder"); - addPathEntry("SF2 directory", m_sf2Dir, + addPathEntry(tr("SF2 directory"), m_sf2Dir, SLOT(setSF2Dir(const QString &)), SLOT(openSF2Dir()), m_sf2DirLineEdit); #ifdef LMMS_HAVE_FLUIDSYNTH - addPathEntry("Default SF2", m_sf2File, + addPathEntry(tr("Default SF2"), m_sf2File, SLOT(setSF2File(const QString &)), SLOT(openSF2File()), m_sf2FileLineEdit); #endif - addPathEntry("GIG directory", m_gigDir, + addPathEntry(tr("GIG directory"), m_gigDir, SLOT(setGIGDir(const QString &)), SLOT(openGIGDir()), m_gigDirLineEdit); - addPathEntry("Theme directory", m_themeDir, + addPathEntry(tr("Theme directory"), m_themeDir, SLOT(setThemeDir(const QString &)), SLOT(openThemeDir()), m_themeDirLineEdit); - addPathEntry("Background artwork", m_backgroundPicFile, + addPathEntry(tr("Background artwork"), m_backgroundPicFile, SLOT(setBackgroundPicFile(const QString &)), SLOT(openBackgroundPicFile()), m_backgroundPicFileLineEdit); @@ -1301,7 +1303,7 @@ void SetupDialog::openGIGDir() { QString new_dir = FileDialog::getExistingDirectory(this, tr("Choose your GIG directory"), m_gigDir); - if(new_dir != QString::null) + if(!new_dir.isEmpty()) { m_gigDirLineEdit->setText(new_dir); } @@ -1318,7 +1320,7 @@ void SetupDialog::openThemeDir() { QString new_dir = FileDialog::getExistingDirectory(this, tr("Choose your theme directory"), m_themeDir); - if(new_dir != QString::null) + if(!new_dir.isEmpty()) { m_themeDirLineEdit->setText(new_dir); } @@ -1353,7 +1355,7 @@ void SetupDialog::openBackgroundPicFile() QString new_file = FileDialog::getOpenFileName(this, tr("Choose your background picture"), dir, "Picture files (" + fileTypes + ")"); - if(new_file != QString::null) + if(!new_file.isEmpty()) { m_backgroundPicFileLineEdit->setText(new_file); } diff --git a/src/gui/dialogs/FileDialog.cpp b/src/gui/dialogs/FileDialog.cpp index 54cc9d6e4d7..848a7f52a7c 100644 --- a/src/gui/dialogs/FileDialog.cpp +++ b/src/gui/dialogs/FileDialog.cpp @@ -84,11 +84,9 @@ QString FileDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &directory, const QString &filter, - QString *selectedFilter, - QFileDialog::Options options) + QString *selectedFilter) { FileDialog dialog(parent, caption, directory, filter); - dialog.setOptions(dialog.options() | options); if (selectedFilter && !selectedFilter->isEmpty()) dialog.selectNameFilter(*selectedFilter); if (dialog.exec() == QDialog::Accepted) { diff --git a/src/gui/dialogs/VersionedSaveDialog.cpp b/src/gui/dialogs/VersionedSaveDialog.cpp index 18993c23bf4..d26f198915a 100644 --- a/src/gui/dialogs/VersionedSaveDialog.cpp +++ b/src/gui/dialogs/VersionedSaveDialog.cpp @@ -31,6 +31,7 @@ #include #include +#include "DeprecationHelper.h" #include "VersionedSaveDialog.h" #include "LedCheckbox.h" @@ -50,8 +51,8 @@ VersionedSaveDialog::VersionedSaveDialog( QWidget *parent, plusButton->setToolTip( tr( "Increment version number" ) ); QPushButton *minusButton( new QPushButton( "-", this ) ); minusButton->setToolTip( tr( "Decrement version number" ) ); - plusButton->setFixedWidth( plusButton->fontMetrics().width( "+" ) + 30 ); - minusButton->setFixedWidth( minusButton->fontMetrics().width( "+" ) + 30 ); + plusButton->setFixedWidth(horizontalAdvance(plusButton->fontMetrics(), "+") + 30); + minusButton->setFixedWidth(horizontalAdvance(minusButton->fontMetrics(), "+") + 30); // Add buttons to grid layout. For doing this, remove the lineEdit and // replace it with a HBox containing lineEdit and the buttons. diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 8c281bf3e1f..b64cea0f5f2 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -45,21 +45,22 @@ #endif #include "ActionGroup.h" -#include "SongEditor.h" -#include "MainWindow.h" +#include "BBTrackContainer.h" +#include "ComboBox.h" +#include "debug.h" +#include "DeprecationHelper.h" #include "GuiApplication.h" +#include "MainWindow.h" #include "embed.h" #include "Engine.h" #include "gui_templates.h" -#include "TimeLineWidget.h" -#include "ToolTip.h" -#include "TextFloat.h" -#include "ComboBox.h" -#include "BBTrackContainer.h" #include "PianoRoll.h" -#include "debug.h" -#include "StringPairDrag.h" #include "ProjectJournal.h" +#include "SongEditor.h" +#include "StringPairDrag.h" +#include "TextFloat.h" +#include "TimeLineWidget.h" +#include "ToolTip.h" QPixmap * AutomationEditor::s_toolDraw = NULL; @@ -320,7 +321,7 @@ void AutomationEditor::updateAfterPatternChange() m_minLevel = m_pattern->firstObject()->minValue(); m_maxLevel = m_pattern->firstObject()->maxValue(); m_step = m_pattern->firstObject()->step(); - m_scrollLevel = ( m_minLevel + m_maxLevel ) / 2; + centerTopBottomScroll(); m_tensionModel->setValue( m_pattern->getTension() ); @@ -1595,12 +1596,36 @@ void AutomationEditor::drawLevelTick(QPainter & p, int tick, float value) p.fillRect( x, y_start, rect_width, rect_height, currentColor ); } - +#ifdef LMMS_DEBUG else { printf("not in range\n"); } +#endif +} + + + +// center the vertical scroll position on the first object's value +void AutomationEditor::centerTopBottomScroll() +{ + // default to the m_scrollLevel position + int pos = static_cast(m_scrollLevel); + // If a pattern exists... + if (m_pattern) + { + // get time map of current pattern + timeMap & time_map = m_pattern->getTimeMap(); + // If time_map is not empty... + if (!time_map.empty()) + { + // set the position to the inverted value ((max + min) - value) + // If we set just (max - value), we're off by m_pattern's minimum + pos = m_pattern->getMax() + m_pattern->getMin() - static_cast(time_map.begin().value()); + } + } + m_topBottomScroll->setValue(pos); } @@ -1632,8 +1657,7 @@ void AutomationEditor::resizeEvent(QResizeEvent * re) m_topBottomScroll->setRange( (int) m_scrollLevel, (int) m_scrollLevel ); } - - m_topBottomScroll->setValue( (int) m_scrollLevel ); + centerTopBottomScroll(); if( Engine::getSong() ) { @@ -1654,11 +1678,11 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::ShiftModifier ) { int y = m_zoomingYModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { y++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { y--; } @@ -1668,11 +1692,11 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::AltModifier ) { int q = m_quantizeModel.value(); - if( we->delta() > 0 ) + if((we->angleDelta().x() + we->angleDelta().y()) > 0) // alt + scroll becomes horizontal scroll on KDE { q--; } - else if( we->delta() < 0 ) + else if((we->angleDelta().x() + we->angleDelta().y()) < 0) // alt + scroll becomes horizontal scroll on KDE { q++; } @@ -1683,17 +1707,17 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier ) { int x = m_zoomingXModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { x++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { x--; } x = qBound( 0, x, m_zoomingXModel.size() - 1 ); - int mouseX = (we->x() - VALUES_WIDTH)* MidiTime::ticksPerBar(); + int mouseX = (position( we ).x() - VALUES_WIDTH)* MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = mouseX / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -1705,16 +1729,22 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) m_zoomingXModel.setValue( x ); } - else if( we->modifiers() & Qt::ShiftModifier - || we->orientation() == Qt::Horizontal ) + + // FIXME: Reconsider if determining orientation is necessary in Qt6. + else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + { + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().x() * 2 / 15); + } + else if(we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue( m_leftRightScroll->value() - - we->delta() * 2 / 15 ); + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().y() * 2 / 15); } else { - m_topBottomScroll->setValue( m_topBottomScroll->value() - - we->delta() / 30 ); + m_topBottomScroll->setValue(m_topBottomScroll->value() - + (we->angleDelta().x() + we->angleDelta().y()) / 30); } } @@ -2326,7 +2356,7 @@ AutomationEditorWindow::AutomationEditorWindow() : zoom_x_label->setPixmap( embed::getIconPixmap( "zoom_x" ) ); m_zoomingXComboBox = new ComboBox( zoomToolBar ); - m_zoomingXComboBox->setFixedSize( 80, 22 ); + m_zoomingXComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingXComboBox->setToolTip( tr( "Horizontal zooming" ) ); for( float const & zoomLevel : m_editor->m_zoomXLevels ) @@ -2345,7 +2375,7 @@ AutomationEditorWindow::AutomationEditorWindow() : zoom_y_label->setPixmap( embed::getIconPixmap( "zoom_y" ) ); m_zoomingYComboBox = new ComboBox( zoomToolBar ); - m_zoomingYComboBox->setFixedSize( 80, 22 ); + m_zoomingYComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingYComboBox->setToolTip( tr( "Vertical zooming" ) ); m_editor->m_zoomingYModel.addItem( "Auto" ); @@ -2375,7 +2405,7 @@ AutomationEditorWindow::AutomationEditorWindow() : quantize_lbl->setPixmap( embed::getIconPixmap( "quantize" ) ); m_quantizeComboBox = new ComboBox( m_toolBar ); - m_quantizeComboBox->setFixedSize( 60, 22 ); + m_quantizeComboBox->setFixedSize( 60, ComboBox::DEFAULT_HEIGHT ); m_quantizeComboBox->setToolTip( tr( "Quantization" ) ); m_quantizeComboBox->setModel( &m_editor->m_quantizeModel ); diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index bfc16df5be5..ffafc4577d8 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -75,7 +75,7 @@ BBEditor::BBEditor( BBTrackContainer* tc ) : DropToolBar *beatSelectionToolBar = addDropToolBarToTop(tr("Beat selector")); m_bbComboBox = new ComboBox( m_toolBar ); - m_bbComboBox->setFixedSize( 200, 22 ); + m_bbComboBox->setFixedSize( 200, ComboBox::DEFAULT_HEIGHT ); m_bbComboBox->setModel( &tc->m_bbComboBoxModel ); beatSelectionToolBar->addWidget( m_bbComboBox ); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 3d35c9c5133..c63225440dc 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -47,11 +47,12 @@ #include "AutomationEditor.h" #include "ActionGroup.h" -#include "ConfigManager.h" #include "BBTrackContainer.h" #include "Clipboard.h" #include "ComboBox.h" +#include "ConfigManager.h" #include "debug.h" +#include "DeprecationHelper.h" #include "DetuningHelper.h" #include "embed.h" #include "GuiApplication.h" @@ -61,9 +62,9 @@ #include "Pattern.h" #include "SongEditor.h" #include "stdshims.h" +#include "StepRecorderWidget.h" #include "TextFloat.h" #include "TimeLineWidget.h" -#include "StepRecorderWidget.h" using std::move; @@ -3321,13 +3322,13 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { we->accept(); // handle wheel events for note edit area - for editing note vol/pan with mousewheel - if( we->x() > noteEditLeft() && we->x() < noteEditRight() - && we->y() > noteEditTop() && we->y() < noteEditBottom() ) + if(position(we).x() > noteEditLeft() && position(we).x() < noteEditRight() + && position(we).y() > noteEditTop() && position(we).y() < noteEditBottom()) { if (!hasValidPattern()) {return;} // get values for going through notes int pixel_range = 8; - int x = we->x() - m_whiteKeyWidth; + int x = position(we).x() - m_whiteKeyWidth; int ticks_start = ( x - pixel_range / 2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x + pixel_range / 2 ) * @@ -3346,7 +3347,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } if( nv.size() > 0 ) { - const int step = we->delta() > 0 ? 1 : -1; + const int step = we->angleDelta().y() > 0 ? 1 : -1; if( m_noteEditMode == NoteEditVolume ) { for ( Note * n : nv ) @@ -3363,7 +3364,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { // show the volume hover-text only if all notes have the // same volume - showVolTextFloat( nv[0]->getVolume(), we->pos(), 1000 ); + showVolTextFloat(nv[0]->getVolume(), position(we), 1000); } } else if( m_noteEditMode == NoteEditPanning ) @@ -3382,7 +3383,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { // show the pan hover-text only if all notes have the same // panning - showPanTextFloat( nv[0]->getPanning(), we->pos(), 1000 ); + showPanTextFloat( nv[0]->getPanning(), position( we ), 1000 ); } } update(); @@ -3394,11 +3395,11 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::AltModifier ) { int q = m_quantizeModel.value(); - if( we->delta() > 0 ) + if((we->angleDelta().x() + we->angleDelta().y()) > 0) // alt + scroll becomes horizontal scroll on KDE { q--; } - else if( we->delta() < 0 ) + else if((we->angleDelta().x() + we->angleDelta().y()) < 0) // alt + scroll becomes horizontal scroll on KDE { q++; } @@ -3408,11 +3409,11 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::ShiftModifier ) { int l = m_noteLenModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { l--; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { l++; } @@ -3422,17 +3423,17 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier ) { int z = m_zoomingModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { z++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { z--; } z = qBound( 0, z, m_zoomingModel.size() - 1 ); - int x = (we->x() - m_whiteKeyWidth)* MidiTime::ticksPerBar(); + int x = (position(we).x() - m_whiteKeyWidth) * MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = x / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -3442,16 +3443,22 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) // update combobox with zooming-factor m_zoomingModel.setValue( z ); } - else if( we->modifiers() & Qt::ShiftModifier - || we->orientation() == Qt::Horizontal ) + + // FIXME: Reconsider if determining orientation is necessary in Qt6. + else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + { + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().x() * 2 / 15); + } + else if(we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue( m_leftRightScroll->value() - - we->delta() * 2 / 15 ); + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().y() * 2 / 15); } else { - m_topBottomScroll->setValue( m_topBottomScroll->value() - - we->delta() / 30 ); + m_topBottomScroll->setValue(m_topBottomScroll->value() - + we->angleDelta().y() / 30); } } @@ -4344,7 +4351,7 @@ PianoRollWindow::PianoRollWindow() : m_zoomingComboBox = new ComboBox( m_toolBar ); m_zoomingComboBox->setModel( &m_editor->m_zoomingModel ); - m_zoomingComboBox->setFixedSize( 64, 22 ); + m_zoomingComboBox->setFixedSize( 64, ComboBox::DEFAULT_HEIGHT ); m_zoomingComboBox->setToolTip( tr( "Horizontal zooming") ); QLabel * zoom_y_lbl = new QLabel(m_toolBar); @@ -4352,7 +4359,7 @@ PianoRollWindow::PianoRollWindow() : m_zoomingYComboBox = new ComboBox(m_toolBar); m_zoomingYComboBox->setModel(&m_editor->m_zoomingYModel); - m_zoomingYComboBox->setFixedSize(64, 22); + m_zoomingYComboBox->setFixedSize(64, ComboBox::DEFAULT_HEIGHT); m_zoomingYComboBox->setToolTip(tr("Vertical zooming")); // setup quantize-stuff @@ -4361,7 +4368,7 @@ PianoRollWindow::PianoRollWindow() : m_quantizeComboBox = new ComboBox( m_toolBar ); m_quantizeComboBox->setModel( &m_editor->m_quantizeModel ); - m_quantizeComboBox->setFixedSize( 64, 22 ); + m_quantizeComboBox->setFixedSize( 64, ComboBox::DEFAULT_HEIGHT ); m_quantizeComboBox->setToolTip( tr( "Quantization") ); // setup note-len-stuff @@ -4370,7 +4377,7 @@ PianoRollWindow::PianoRollWindow() : m_noteLenComboBox = new ComboBox( m_toolBar ); m_noteLenComboBox->setModel( &m_editor->m_noteLenModel ); - m_noteLenComboBox->setFixedSize( 105, 22 ); + m_noteLenComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_noteLenComboBox->setToolTip( tr( "Note length") ); // setup scale-stuff @@ -4379,7 +4386,7 @@ PianoRollWindow::PianoRollWindow() : m_scaleComboBox = new ComboBox( m_toolBar ); m_scaleComboBox->setModel( &m_editor->m_scaleModel ); - m_scaleComboBox->setFixedSize( 105, 22 ); + m_scaleComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_scaleComboBox->setToolTip( tr( "Scale") ); // setup chord-stuff @@ -4388,7 +4395,7 @@ PianoRollWindow::PianoRollWindow() : m_chordComboBox = new ComboBox( m_toolBar ); m_chordComboBox->setModel( &m_editor->m_chordModel ); - m_chordComboBox->setFixedSize( 105, 22 ); + m_chordComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_chordComboBox->setToolTip( tr( "Chord" ) ); // -- Clear ghost pattern button diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index d60b986d898..492de0e0092 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -24,7 +24,6 @@ #include "SongEditor.h" -#include #include #include #include @@ -32,24 +31,26 @@ #include #include #include +#include +#include "AudioDevice.h" #include "AutomatableSlider.h" #include "ComboBox.h" #include "ConfigManager.h" #include "CPULoadWidget.h" +#include "DeprecationHelper.h" #include "embed.h" #include "GuiApplication.h" #include "LcdSpinBox.h" #include "MainWindow.h" #include "MeterDialog.h" #include "Mixer.h" +#include "Oscilloscope.h" +#include "PianoRoll.h" #include "TextFloat.h" +#include "TimeDisplayWidget.h" #include "TimeLineWidget.h" #include "ToolTip.h" -#include "Oscilloscope.h" -#include "TimeDisplayWidget.h" -#include "AudioDevice.h" -#include "PianoRoll.h" #include "Track.h" const QVector SongEditor::m_zoomLevels = @@ -527,18 +528,18 @@ void SongEditor::wheelEvent( QWheelEvent * we ) { int z = m_zoomingModel->value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { z++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { z--; } z = qBound( 0, z, m_zoomingModel->size() - 1 ); - int x = we->x() - m_trackHeadWidth; + int x = position(we).x() - m_trackHeadWidth; // bar based on the mouse x-position where the scroll wheel was used int bar = x / pixelsPerBar(); // what would be the bar in the new zoom level on the very same mouse x @@ -555,10 +556,17 @@ void SongEditor::wheelEvent( QWheelEvent * we ) // and make sure, all TCO's are resized and relocated realignTracks(); } - else if( we->modifiers() & Qt::ShiftModifier || we->orientation() == Qt::Horizontal ) + + // FIXME: Reconsider if determining orientation is necessary in Qt6. + else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + { + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().x() /30); + } + else if(we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue( m_leftRightScroll->value() - - we->delta() / 30 ); + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().y() / 30); } else { @@ -955,7 +963,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : //Set up zooming-stuff m_zoomingComboBox = new ComboBox( m_toolBar ); - m_zoomingComboBox->setFixedSize( 80, 22 ); + m_zoomingComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingComboBox->move( 580, 4 ); m_zoomingComboBox->setModel(m_editor->m_zoomingModel); m_zoomingComboBox->setToolTip(tr("Horizontal zooming")); @@ -970,7 +978,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : //Set up quantization/snapping selector m_snappingComboBox = new ComboBox( m_toolBar ); - m_snappingComboBox->setFixedSize( 80, 22 ); + m_snappingComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_snappingComboBox->setModel(m_editor->m_snappingModel); m_snappingComboBox->setToolTip(tr("Clip snapping size")); connect(m_editor->snappingModel(), SIGNAL(dataChanged()), this, SLOT(updateSnapLabel())); diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index c796bfa74c8..4ef43bee74e 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -51,6 +51,8 @@ ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : m_menu( this ), m_pressed( false ) { + setFixedHeight( ComboBox::DEFAULT_HEIGHT ); + if( s_background == NULL ) { s_background = new QPixmap( embed::getIconPixmap( "combobox_bg" ) ); @@ -192,7 +194,7 @@ void ComboBox::paintEvent( QPaintEvent * _pe ) // Border QStyleOptionFrame opt; opt.initFrom( this ); - opt.state = 0; + opt.state = QStyle::StateFlag::State_None; style()->drawPrimitive( QStyle::PE_Frame, &opt, &p, this ); @@ -230,7 +232,7 @@ void ComboBox::wheelEvent( QWheelEvent* event ) { if( model() ) { - model()->setInitValue( model()->value() + ( ( event->delta() < 0 ) ? 1 : -1 ) ); + model()->setInitValue(model()->value() + ((event->angleDelta().y() < 0) ? 1 : -1)); update(); event->accept(); } diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp index 15b4e0d282a..98aaf0e0eee 100644 --- a/src/gui/widgets/Controls.cpp +++ b/src/gui/widgets/Controls.cpp @@ -78,7 +78,7 @@ ComboControl::ComboControl(QWidget *parent) : m_combo(new ComboBox(nullptr)), m_label(new QLabel(m_widget)) { - m_combo->setFixedSize(64, 22); + m_combo->setFixedSize(64, ComboBox::DEFAULT_HEIGHT); QVBoxLayout* vbox = new QVBoxLayout(m_widget); vbox->addWidget(m_combo); vbox->addWidget(m_label); diff --git a/src/gui/widgets/FadeButton.cpp b/src/gui/widgets/FadeButton.cpp index 43f8061441a..c40569c2181 100644 --- a/src/gui/widgets/FadeButton.cpp +++ b/src/gui/widgets/FadeButton.cpp @@ -110,20 +110,20 @@ void FadeButton::paintEvent(QPaintEvent * _pe) { QColor col = m_normalColor; - if(!m_stateTimer.isNull() && m_stateTimer.elapsed() < FadeDuration) + if(m_stateTimer.isValid() && m_stateTimer.elapsed() < FadeDuration) { // The first part of the fade, when a note is triggered. col = fadeToColor(m_activatedColor, m_holdColor, m_stateTimer, FadeDuration); QTimer::singleShot(20, this, SLOT(update())); } - else if (!m_stateTimer.isNull() + else if (m_stateTimer.isValid() && m_stateTimer.elapsed() >= FadeDuration && activeNotes > 0) { // The fade is done, but at least one note is still held. col = m_holdColor; } - else if (!m_releaseTimer.isNull() && m_releaseTimer.elapsed() < FadeDuration) + else if (m_releaseTimer.isValid() && m_releaseTimer.elapsed() < FadeDuration) { // Last note just ended. Fade to default color. col = fadeToColor(m_holdColor, m_normalColor, m_releaseTimer, FadeDuration); @@ -149,7 +149,7 @@ void FadeButton::paintEvent(QPaintEvent * _pe) } -QColor FadeButton::fadeToColor(QColor startCol, QColor endCol, QTime timer, float duration) +QColor FadeButton::fadeToColor(QColor startCol, QColor endCol, QElapsedTimer timer, float duration) { QColor col; diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 4317066ab65..1f0a13ec173 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -265,7 +265,7 @@ void Fader::wheelEvent ( QWheelEvent *ev ) { ev->accept(); - if ( ev->delta() > 0 ) + if (ev->angleDelta().y() > 0) { model()->incValue( 1 ); } @@ -282,7 +282,7 @@ void Fader::wheelEvent ( QWheelEvent *ev ) /// /// Set peak value (0.0 .. 1.0) /// -void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTime &lastPeakTime ) +void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QElapsedTimer &lastPeakTimer ) { if( fPeak < m_fMinPeak ) { @@ -299,12 +299,12 @@ void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTim if( targetPeak >= persistentPeak ) { persistentPeak = targetPeak; - lastPeakTime.restart(); + lastPeakTimer.restart(); } update(); } - if( persistentPeak > 0 && lastPeakTime.elapsed() > 1500 ) + if( persistentPeak > 0 && lastPeakTimer.elapsed() > 1500 ) { persistentPeak = qMax( 0, persistentPeak-0.05 ); update(); @@ -315,14 +315,14 @@ void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTim void Fader::setPeak_L( float fPeak ) { - setPeak( fPeak, m_fPeakValue_L, m_persistentPeak_L, m_lastPeakTime_L ); + setPeak( fPeak, m_fPeakValue_L, m_persistentPeak_L, m_lastPeakTimer_L ); } void Fader::setPeak_R( float fPeak ) { - setPeak( fPeak, m_fPeakValue_R, m_persistentPeak_R, m_lastPeakTime_R ); + setPeak( fPeak, m_fPeakValue_R, m_persistentPeak_R, m_lastPeakTimer_R ); } diff --git a/src/gui/widgets/InstrumentSoundShapingView.cpp b/src/gui/widgets/InstrumentSoundShapingView.cpp index a6c6fbd56cf..24a52fad235 100644 --- a/src/gui/widgets/InstrumentSoundShapingView.cpp +++ b/src/gui/widgets/InstrumentSoundShapingView.cpp @@ -62,7 +62,7 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : { m_envLfoViews[i] = new EnvelopeAndLfoView( m_targetsTabWidget ); m_targetsTabWidget->addTab( m_envLfoViews[i], - tr( InstrumentSoundShaping::targetNames[i][0].toUtf8().constData() ), + tr( InstrumentSoundShaping::targetNames[i][0] ), NULL ); } @@ -74,7 +74,7 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : m_filterComboBox = new ComboBox( m_filterGroupBox ); - m_filterComboBox->setGeometry( 14, 22, 120, 22 ); + m_filterComboBox->setGeometry( 14, 22, 120, ComboBox::DEFAULT_HEIGHT ); m_filterComboBox->setFont( pointSize<8>( m_filterComboBox->font() ) ); diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 920c9765ddb..e3d22f69ab4 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -38,6 +38,7 @@ #include "CaptionMenu.h" #include "ConfigManager.h" #include "ControllerConnection.h" +#include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" #include "GuiApplication.h" @@ -168,9 +169,9 @@ void Knob::setLabel( const QString & txt ) m_label = txt; if( m_knobPixmap ) { - setFixedSize( qMax( m_knobPixmap->width(), - QFontMetrics( pointSizeF( font(), 6.5) ).width( m_label ) ), - m_knobPixmap->height() + 10 ); + setFixedSize(qMax( m_knobPixmap->width(), + horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)), + m_knobPixmap->height() + 10); } update(); } @@ -682,20 +683,20 @@ void Knob::paintEvent( QPaintEvent * _me ) p.fontMetrics().width( m_label ) / 2 + 1, height() - 1, m_label );*/ p.setPen( textColor() ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2, - height() - 2, m_label ); + p.drawText(width() / 2 - + horizontalAdvance(p.fontMetrics(), m_label) / 2, + height() - 2, m_label); } } -void Knob::wheelEvent( QWheelEvent * _we ) +void Knob::wheelEvent(QWheelEvent * we) { - _we->accept(); + we->accept(); const float stepMult = model()->range() / 2000 / model()->step(); - const int inc = ( ( _we->delta() > 0 ) ? 1 : -1 ) * ( ( stepMult < 1 ) ? 1 : stepMult ); + const int inc = ((we->angleDelta().y() > 0 ) ? 1 : -1) * ((stepMult < 1 ) ? 1 : stepMult); model()->incValue( inc ); diff --git a/src/gui/widgets/LcdSpinBox.cpp b/src/gui/widgets/LcdSpinBox.cpp index 446639090f6..6aacc01dd5a 100644 --- a/src/gui/widgets/LcdSpinBox.cpp +++ b/src/gui/widgets/LcdSpinBox.cpp @@ -149,11 +149,10 @@ void LcdSpinBox::mouseReleaseEvent( QMouseEvent* ) -void LcdSpinBox::wheelEvent( QWheelEvent * _we ) +void LcdSpinBox::wheelEvent(QWheelEvent * we) { - _we->accept(); - model()->setInitValue( model()->value() + - ( ( _we->delta() > 0 ) ? 1 : -1 ) * model()->step() ); + we->accept(); + model()->setInitValue(model()->value() + ((we->angleDelta().y() > 0) ? 1 : -1) * model()->step()); emit manualChange(); } diff --git a/src/gui/widgets/LcdWidget.cpp b/src/gui/widgets/LcdWidget.cpp index e34e1eb47b3..63e3b6ac0c5 100644 --- a/src/gui/widgets/LcdWidget.cpp +++ b/src/gui/widgets/LcdWidget.cpp @@ -32,6 +32,7 @@ #include #include "LcdWidget.h" +#include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" #include "MainWindow.h" @@ -199,13 +200,13 @@ void LcdWidget::paintEvent( QPaintEvent* ) { p.setFont( pointSizeF( p.font(), 6.5 ) ); p.setPen( textShadowColor() ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2 + 1, - height(), m_label ); + p.drawText(width() / 2 - + horizontalAdvance(p.fontMetrics(), m_label) / 2 + 1, + height(), m_label); p.setPen( textColor() ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2, - height() - 1, m_label ); + p.drawText(width() / 2 - + horizontalAdvance(p.fontMetrics(), m_label) / 2, + height() - 1, m_label); } } @@ -240,10 +241,10 @@ void LcdWidget::updateSize() m_cellHeight + (2*margin) ); } else { - setFixedSize( qMax( + setFixedSize(qMax( m_cellWidth * m_numDigits + 2*(margin+m_marginWidth), - QFontMetrics( pointSizeF( font(), 6.5 ) ).width( m_label ) ), - m_cellHeight + (2*margin) + 9 ); + horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)), + m_cellHeight + (2*margin) + 9); } update(); diff --git a/src/gui/widgets/LedCheckbox.cpp b/src/gui/widgets/LedCheckbox.cpp index bdb537744f7..1ce64f1a85e 100644 --- a/src/gui/widgets/LedCheckbox.cpp +++ b/src/gui/widgets/LedCheckbox.cpp @@ -27,6 +27,7 @@ #include #include "LedCheckbox.h" +#include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" @@ -120,7 +121,9 @@ void LedCheckBox::initUi( LedColors _color ) void LedCheckBox::onTextUpdated() { - setFixedSize( m_ledOffPixmap->width() + 5 + QFontMetrics( font() ).width( text() ), m_ledOffPixmap->height() ); + setFixedSize(m_ledOffPixmap->width() + 5 + horizontalAdvance(QFontMetrics(font()), + text()), + m_ledOffPixmap->height()); } diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index 22d32261210..07889c331f5 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -31,8 +31,9 @@ #include #include -#include "gui_templates.h" +#include "DeprecationHelper.h" #include "embed.h" +#include "gui_templates.h" TabWidget::TabWidget(const QString & caption, QWidget * parent, bool usePixmap, bool resizable) : @@ -76,7 +77,7 @@ void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, i } // Tab's width when it is a text tab. This isn't correct for artwork tabs, but it's fixed later during the PaintEvent - int tab_width = fontMetrics().width( name ) + 10; + int tab_width = horizontalAdvance(fontMetrics(), name) + 10; // Register new tab widgetDesc d = { w, pixmap, name, tab_width }; @@ -125,7 +126,7 @@ int TabWidget::findTabAtPos( const QPoint *pos ) if( pos->y() > 1 && pos->y() < m_tabbarHeight - 1 ) { - int cx = ( ( m_caption == "" ) ? 4 : 14 ) + fontMetrics().width( m_caption ); + int cx = ((m_caption == "") ? 4 : 14) + horizontalAdvance(fontMetrics(), m_caption); for( widgetStack::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it ) { @@ -232,7 +233,7 @@ void TabWidget::paintEvent( QPaintEvent * pe ) } // Calculate the tabs' x (tabs are painted next to the caption) - int tab_x_offset = m_caption.isEmpty() ? 4 : 14 + fontMetrics().width( m_caption ); + int tab_x_offset = m_caption.isEmpty() ? 4 : 14 + horizontalAdvance(fontMetrics(), m_caption); // Compute tabs' width depending on the number of tabs (only applicable for artwork tabs) widgetStack::iterator first = m_widgets.begin(); @@ -288,13 +289,13 @@ void TabWidget::paintEvent( QPaintEvent * pe ) // Switch between tabs with mouse wheel void TabWidget::wheelEvent( QWheelEvent * we ) { - if( we->y() > m_tabheight ) + if(position(we).y() > m_tabheight) { return; } we->accept(); - int dir = ( we->delta() < 0 ) ? 1 : -1; + int dir = (we->angleDelta().y() < 0) ? 1 : -1; int tab = m_activeTab; while( tab > -1 && static_cast( tab ) < m_widgets.count() ) { diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index b40b6e65785..5a51bb8aced 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -76,11 +76,6 @@ #include "TrackLabelButton.h" -const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack", - "With this knob you can set " - "the volume of the opened " - "channel."); - const int INSTRUMENT_WIDTH = 254; const int INSTRUMENT_HEIGHT = INSTRUMENT_WIDTH; const int PIANO_HEIGHT = 80; diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index bcb18bd5990..e5de8b83b68 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -24,23 +24,21 @@ */ #include "Pattern.h" +#include #include #include #include #include #include -#include "InstrumentTrack.h" -#include "gui_templates.h" +#include "AudioSampleRecorder.h" +#include "BBTrackContainer.h" +#include "DeprecationHelper.h" #include "embed.h" #include "GuiApplication.h" +#include "InstrumentTrack.h" #include "PianoRoll.h" #include "RenameDialog.h" -#include "SampleBuffer.h" -#include "AudioSampleRecorder.h" -#include "BBTrackContainer.h" -#include "StringPairDrag.h" -#include "MainWindow.h" #include @@ -778,16 +776,16 @@ void PatternView::mouseDoubleClickEvent(QMouseEvent *_me) -void PatternView::wheelEvent( QWheelEvent * _we ) +void PatternView::wheelEvent(QWheelEvent * we) { - if( m_pat->m_patternType == Pattern::BeatPattern && - ( fixedTCOs() || pixelsPerBar() >= 96 ) && - _we->y() > height() - s_stepBtnOff->height() ) + if(m_pat->m_patternType == Pattern::BeatPattern && + (fixedTCOs() || pixelsPerBar() >= 96) && + position(we).y() > height() - s_stepBtnOff->height()) { // get the step number that was wheeled on and // do calculations in floats to prevent rounding errors... - float tmp = ( ( float(_we->x()) - TCO_BORDER_WIDTH ) * - float( m_pat -> m_steps ) ) / float(width() - TCO_BORDER_WIDTH*2); + float tmp = ((float(position(we).x()) - TCO_BORDER_WIDTH) * + float(m_pat -> m_steps)) / float(width() - TCO_BORDER_WIDTH*2); int step = int( tmp ); @@ -797,7 +795,7 @@ void PatternView::wheelEvent( QWheelEvent * _we ) } Note * n = m_pat->noteAtStep( step ); - if( !n && _we->delta() > 0 ) + if(!n && we->angleDelta().y() > 0) { n = m_pat->addStepNote( step ); n->setVolume( 0 ); @@ -806,7 +804,7 @@ void PatternView::wheelEvent( QWheelEvent * _we ) { int vol = n->getVolume(); - if( _we->delta() > 0 ) + if(we->angleDelta().y() > 0) { n->setVolume( qMin( 100, vol + 5 ) ); } @@ -822,11 +820,11 @@ void PatternView::wheelEvent( QWheelEvent * _we ) gui->pianoRoll()->update(); } } - _we->accept(); + we->accept(); } else { - TrackContentObjectView::wheelEvent( _we ); + TrackContentObjectView::wheelEvent(we); } } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 0183684a180..d73b62cbaff 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -358,26 +358,26 @@ void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) { contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( individualTCO - ? "Delete (middle mousebutton)" - : "Delete selection (middle mousebutton)" ), + individualTCO + ? tr("Delete (middle mousebutton)") + : tr("Delete selection (middle mousebutton)"), [this](){ contextMenuAction( Remove ); } ); contextMenu.addSeparator(); contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( individualTCO - ? "Cut" - : "Cut selection" ), + individualTCO + ? tr("Cut") + : tr("Cut selection"), [this](){ contextMenuAction( Cut ); } ); } contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( individualTCO - ? "Copy" - : "Copy selection" ), + individualTCO + ? tr("Copy") + : tr("Copy selection"), [this](){ contextMenuAction( Copy ); } ); contextMenu.addAction( @@ -389,9 +389,9 @@ void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( individualTCO - ? "Mute/unmute (<%1> + middle click)" - : "Mute/unmute selection (<%1> + middle click)" ).arg(UI_CTRL_KEY), + (individualTCO + ? tr("Mute/unmute (<%1> + middle click)") + : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), [this](){ contextMenuAction( Mute ); } ); /*contextMenu.addAction( embed::getIconPixmap( "record" ), diff --git a/tests/src/core/ProjectVersionTest.cpp b/tests/src/core/ProjectVersionTest.cpp index 7c99727397f..e52088f6f8c 100644 --- a/tests/src/core/ProjectVersionTest.cpp +++ b/tests/src/core/ProjectVersionTest.cpp @@ -39,9 +39,39 @@ private slots: QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Minor) == "1.1.5"); QVERIFY( ! ( ProjectVersion("3.1.0", ProjectVersion::Minor) < "2.2.5" ) ); QVERIFY( ! ( ProjectVersion("2.5.0", ProjectVersion::Release) < "2.2.5" ) ); + //A pre-release version has lower precedence than a normal version QVERIFY(ProjectVersion("1.1.0") > "1.1.0-alpha"); + //But higher precedence than the previous version + QVERIFY(ProjectVersion("1.1.0-alpha") > "1.0.0"); + //Identifiers with letters or hyphens are compare lexically in ASCII sort order QVERIFY(ProjectVersion("1.1.0-alpha") < "1.1.0-beta"); QVERIFY(ProjectVersion("1.2.0-rc1") < "1.2.0-rc2"); + //Build metadata MUST be ignored when determining version precedence + QVERIFY(ProjectVersion("1.2.2") == "1.2.2+metadata"); + QVERIFY(ProjectVersion("1.0.0-alpha") < "1.0.0-alpha.1"); + QVERIFY(ProjectVersion("1.0.0-alpha.1") < "1.0.0-alpha.beta"); + QVERIFY(ProjectVersion("1.0.0-alpha.beta") < "1.0.0-beta"); + QVERIFY(ProjectVersion("1.0.0-beta.2") < "1.0.0-beta.11"); + //Test workaround for old, nonstandard version numbers + QVERIFY(ProjectVersion("1.2.2.42") == "1.2.3-42"); + QVERIFY(ProjectVersion("1.2.2.42") > "1.2.2.21"); + //Ensure that newer versions of the same format aren't upgraded + //in order to discourage use of incorrect versioning + QVERIFY(ProjectVersion("1.2.3.42") == "1.2.3"); + //CompareVersion "All" should compare every identifier + QVERIFY( + ProjectVersion("1.0.0-a.b.c.d.e.f.g.h.i.j.k.l", ProjectVersion::All) + < "1.0.0-a.b.c.d.e.f.g.h.i.j.k.m" + ); + //Prerelease identifiers may contain hyphens + QVERIFY(ProjectVersion("1.0.0-Alpha-1.2") > "1.0.0-Alpha-1.1"); + //We shouldn't crash on invalid versions + QVERIFY(ProjectVersion("1-invalid") == "1.0.0-invalid"); + QVERIFY(ProjectVersion("") == "0.0.0"); + //Numeric identifiers are smaller than non-numeric identiiers + QVERIFY(ProjectVersion("1.0.0-alpha") > "1.0.0-1"); + //An identifier of the form "-x" is non-numeric, not negative + QVERIFY(ProjectVersion("1.0.0-alpha.-1") > "1.0.0-alpha.1"); } } ProjectVersionTests; From 090a75df1bda53364b6bafd91f5a86359600a108 Mon Sep 17 00:00:00 2001 From: Spekular Date: Sun, 20 Sep 2020 16:48:59 +0200 Subject: [PATCH 5/8] Formatting changes from review --- include/AutomationPattern.h | 2 +- include/ProjectVersion.h | 1 + src/core/DataFile.cpp | 53 ++++++++++++++----------------------- src/tracks/BBTrack.cpp | 26 +++++++++--------- 4 files changed, 35 insertions(+), 47 deletions(-) diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index aad991bb362..9e3a08c5ade 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -184,7 +184,7 @@ public slots: ProgressionTypes m_progressionType; bool m_dragging; - + bool m_isRecording; float m_lastRecordedValue; diff --git a/include/ProjectVersion.h b/include/ProjectVersion.h index e938ccc81c7..94ee9c6ba89 100644 --- a/include/ProjectVersion.h +++ b/include/ProjectVersion.h @@ -45,6 +45,7 @@ class ProjectVersion ProjectVersion(QString version, CompareType c = All); ProjectVersion(const char * version, CompareType c = All); + const QString& getVersion() const { return m_version; } int getMajor() const { return m_major; } int getMinor() const { return m_minor; } int getPatch() const { return m_patch; } diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 326f01533e7..ca1f061eda3 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1365,7 +1365,7 @@ void DataFile::upgrade_noHiddenClipNames() { QDomElement clip = clips.item(j).toElement(); QString clipName = clip.attribute("name", ""); - if (clipName == trackName){ clip.setAttribute("name", ""); } + if (clipName == trackName) { clip.setAttribute("name", ""); } } }; @@ -1474,45 +1474,32 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) } } - if( root.hasAttribute( "creatorversion" ) ) + if(root.hasAttribute("creatorversion")) { // compareType defaults to All, so it doesn't have to be set here - ProjectVersion createdWith = root.attribute( "creatorversion" ); + ProjectVersion createdWith = root.attribute("creatorversion"); ProjectVersion openedWith = LMMS_VERSION; - if ( createdWith != openedWith ) - { - if( createdWith.setCompareType( ProjectVersion::Minor ) != - openedWith.setCompareType( ProjectVersion::Minor ) ) - { - if( gui != nullptr && root.attribute( "type" ) == "song" ) - { - TextFloat::displayMessage( - SongEditor::tr( "Version difference" ), - SongEditor::tr( - "This %1 was created with " - "LMMS %2." - ).arg( - _sourceFile.endsWith( ".mpt" ) ? - SongEditor::tr( "template" ) : - SongEditor::tr( "project" ) - ) - .arg( root.attribute( "creatorversion" ) ), - embed::getIconPixmap( "whatsthis", 24, 24 ), - 2500 - ); - } - } - - // the upgrade needs to happen after the warning as it updates the project version. - if( createdWith.setCompareType( ProjectVersion::All ) < - openedWith.setCompareType( ProjectVersion::All ) ) - { upgrade(); } + if (createdWith < openedWith) { upgrade(); } + + if (createdWith.setCompareType(ProjectVersion::Minor) + != openedWith.setCompareType(ProjectVersion::Minor) + && gui != nullptr && root.attribute("type") == "song" + ){ + auto projectType = _sourceFile.endsWith(".mpt") ? + SongEditor::tr("template") : SongEditor::tr("project"); + + TextFloat::displayMessage( + SongEditor::tr("Version difference"), + SongEditor::tr("This %1 was created with LMMS %2") + .arg(projectType).arg(createdWith.getVersion()), + embed::getIconPixmap("whatsthis", 24, 24), + 2500 + ); } } - m_content = root.elementsByTagName( typeName( m_type ) ). - item( 0 ).toElement(); + m_content = root.elementsByTagName(typeName(m_type)).item(0).toElement(); } diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 498a904afeb..ec6b4042004 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -75,7 +75,7 @@ void BBTCO::saveSettings( QDomDocument & doc, QDomElement & element ) element.setAttribute( "len", length() ); element.setAttribute( "muted", isMuted() ); element.setAttribute( "color", color() ); - + if( m_useStyleColor ) { element.setAttribute( "usestyle", 1 ); @@ -106,10 +106,10 @@ void BBTCO::loadSettings( const QDomElement & element ) { setColor( QColor( element.attribute( "color" ).toUInt() ) ); } - + if( element.hasAttribute( "usestyle" ) ) { - if( element.attribute( "usestyle" ).toUInt() == 1 ) + if( element.attribute( "usestyle" ).toUInt() == 1 ) { m_useStyleColor = true; } @@ -212,12 +212,12 @@ void BBTCOView::paintEvent( QPaintEvent * ) QLinearGradient lingrad( 0, 0, 0, height() ); QColor c; bool muted = m_bbTCO->getTrack()->isMuted() || m_bbTCO->isMuted(); - + // state: selected, muted, default, user selected - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() - : ( m_bbTCO->m_useStyleColor ? painter.background().color() + c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() + : ( m_bbTCO->m_useStyleColor ? painter.background().color() : m_bbTCO->colorObj() ) ); - + lingrad.setColorAt( 0, c.lighter( 130 ) ); lingrad.setColorAt( 1, c.lighter( 70 ) ); @@ -232,7 +232,7 @@ void BBTCOView::paintEvent( QPaintEvent * ) { p.fillRect( rect(), c ); } - + // bar lines const int lineSize = 3; p.setPen( c.darker( 200 ) ); @@ -256,12 +256,12 @@ void BBTCOView::paintEvent( QPaintEvent * ) // inner border p.setPen( c.lighter( 130 ) ); p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, - rect().bottom() - TCO_BORDER_WIDTH ); + rect().bottom() - TCO_BORDER_WIDTH ); // outer border p.setPen( c.darker( 300 ) ); p.drawRect( 0, 0, rect().right(), rect().bottom() ); - + // draw the 'muted' pixmap only if the pattern was manualy muted if( m_bbTCO->isMuted() ) { @@ -270,11 +270,11 @@ void BBTCOView::paintEvent( QPaintEvent * ) p.drawPixmap( spacing, height() - ( size + spacing ), embed::getIconPixmap( "muted", size, size ) ); } - + p.end(); - + painter.drawPixmap( 0, 0, m_paintPixmap ); - + } From 8957a133897f1ef484a263370cc10c7319af8b22 Mon Sep 17 00:00:00 2001 From: Spekular Date: Sun, 20 Sep 2020 17:08:28 +0200 Subject: [PATCH 6/8] Change weird for loop conditions --- src/core/DataFile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index ca1f061eda3..1fe67601356 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1361,7 +1361,7 @@ void DataFile::upgrade_noHiddenClipNames() auto clearDefaultNames = [](QDomNodeList clips, QString trackName) { - for (int j = 0; !clips.item(j).isNull(); ++j) + for (int j = 0; j < clips.size(); ++j) { QDomElement clip = clips.item(j).toElement(); QString clipName = clip.attribute("name", ""); @@ -1369,7 +1369,7 @@ void DataFile::upgrade_noHiddenClipNames() } }; - for (int i = 0; !tracks.item(i).isNull(); ++i) + for (int i = 0; i < tracks.size(); ++i) { QDomElement track = tracks.item(i).toElement(); QString trackName = track.attribute("name", ""); From 0e7d066a9848f3b63b2a828cfbb858bc48d53d7f Mon Sep 17 00:00:00 2001 From: Spekular Date: Sun, 20 Sep 2020 17:18:56 +0200 Subject: [PATCH 7/8] Properly revert AutomationPatter.h changes --- include/AutomationPattern.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 9e3a08c5ade..cad9d0a1d00 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -184,7 +184,7 @@ public slots: ProgressionTypes m_progressionType; bool m_dragging; - + bool m_isRecording; float m_lastRecordedValue; From 5d98a85d472250f4605d48b8b85222d549e9a367 Mon Sep 17 00:00:00 2001 From: Spekular Date: Mon, 21 Sep 2020 02:15:51 +0200 Subject: [PATCH 8/8] Only clear names that match our parent track, be more generous with use of legacyFileVersion --- src/core/DataFile.cpp | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 1fe67601356..c1b48af38c8 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1374,9 +1374,9 @@ void DataFile::upgrade_noHiddenClipNames() QDomElement track = tracks.item(i).toElement(); QString trackName = track.attribute("name", ""); - QDomNodeList instClips = elementsByTagName("pattern"); - QDomNodeList autoClips = elementsByTagName("automationpattern"); - QDomNodeList bbClips = elementsByTagName("bbtco"); + QDomNodeList instClips = track.elementsByTagName("pattern"); + QDomNodeList autoClips = track.elementsByTagName("automationpattern"); + QDomNodeList bbClips = track.elementsByTagName("bbtco"); clearDefaultNames(instClips, trackName); clearDefaultNames(autoClips, trackName); @@ -1459,22 +1459,20 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) m_type = type( root.attribute( "type" ) ); m_head = root.elementsByTagName( "head" ).item( 0 ).toElement(); - if( root.hasAttribute( "version" ) ) + if (!root.hasAttribute("version") || root.attribute("version")=="1.0") { - if( root.attribute( "version" ) == "1.0" ){ - // The file versioning is now a unsigned int, not maj.min, so we use - // legacyFileVersion() to retrieve the appropriate version - m_fileVersion = legacyFileVersion(); - } - else - { - bool success; - m_fileVersion = root.attribute( "version" ).toUInt( &success ); - if( !success ) qWarning("File Version conversion failure."); - } + // The file versioning is now a unsigned int, not maj.min, so we use + // legacyFileVersion() to retrieve the appropriate version + m_fileVersion = legacyFileVersion(); + } + else + { + bool success; + m_fileVersion = root.attribute( "version" ).toUInt( &success ); + if( !success ) qWarning("File Version conversion failure."); } - if(root.hasAttribute("creatorversion")) + if (root.hasAttribute("creatorversion")) { // compareType defaults to All, so it doesn't have to be set here ProjectVersion createdWith = root.attribute("creatorversion");