From 8a19c2d25e8b91640de87557ecd5793ba4aebefe Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 5 Sep 2020 22:33:51 +0200 Subject: [PATCH 1/5] Fix spelling in Lv2 classes --- include/LinkedModelGroupViews.h | 2 +- include/LinkedModelGroups.h | 2 +- include/Lv2ControlBase.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 From d29066fa83adbd1e5ecec6573c0583cc66d67ea9 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 5 Sep 2020 23:27:13 +0200 Subject: [PATCH 2/5] Add helpful comment --- src/core/lv2/Lv2Proc.cpp | 2 ++ 1 file changed, 2 insertions(+) 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; From 3d8b31039f0fec7800b14215b47cc20ed648ab16 Mon Sep 17 00:00:00 2001 From: Dat Ng Date: Sun, 13 Sep 2020 04:09:46 +0200 Subject: [PATCH 3/5] Qt deprecation fix (#5619) Qt6 TODO: Orientation check by comparing angleDelta().x() and y() won't make sense because the direction is arbitrary in Qt 6. --- include/DeprecationHelper.h | 63 +++++++++++++++++++ include/FadeButton.h | 8 +-- include/Fader.h | 11 ++-- include/FileDialog.h | 3 +- include/MainWindow.h | 2 +- include/StepRecorder.h | 6 +- include/SubWindow.h | 2 +- plugins/GigPlayer/PatchesDialog.h | 2 +- .../audio_file_processor.cpp | 2 +- plugins/ladspa_browser/ladspa_port_dialog.cpp | 2 +- plugins/lb302/lb302.cpp | 9 ++- plugins/sf2_player/patches_dialog.h | 2 +- src/core/PluginFactory.cpp | 5 ++ src/core/Song.cpp | 4 ++ src/core/audio/AudioFileOgg.cpp | 11 +++- src/gui/SetupDialog.cpp | 6 +- src/gui/dialogs/FileDialog.cpp | 4 +- src/gui/dialogs/VersionedSaveDialog.cpp | 5 +- src/gui/editors/AutomationEditor.cpp | 51 ++++++++------- src/gui/editors/PianoRoll.cpp | 49 ++++++++------- src/gui/editors/SongEditor.cpp | 30 +++++---- src/gui/widgets/ComboBox.cpp | 4 +- src/gui/widgets/FadeButton.cpp | 8 +-- src/gui/widgets/Fader.cpp | 12 ++-- 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/Pattern.cpp | 32 +++++----- 30 files changed, 253 insertions(+), 143 deletions(-) create mode 100644 include/DeprecationHelper.h 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/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/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/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/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index dbde6e8c45c..da0f17a3c9e 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -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/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 72bd5a4946e..cc671749db1 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -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/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/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/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/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/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index f06eea9e05c..86ba61eb108 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -1303,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); } @@ -1320,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); } @@ -1355,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 af9ea3b08a4..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; @@ -1677,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--; } @@ -1691,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++; } @@ -1706,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 @@ -1728,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); } } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 0dc659dceb8..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); } } diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index e738708ac09..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 { diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index 69955501d61..4ef43bee74e 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -194,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 ); @@ -232,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/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/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/Pattern.cpp b/src/tracks/Pattern.cpp index 40f11b3cb30..52259c70733 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 @@ -782,16 +780,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 ); @@ -801,7 +799,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 ); @@ -810,7 +808,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 ) ); } @@ -826,11 +824,11 @@ void PatternView::wheelEvent( QWheelEvent * _we ) gui->pianoRoll()->update(); } } - _we->accept(); + we->accept(); } else { - TrackContentObjectView::wheelEvent( _we ); + TrackContentObjectView::wheelEvent(we); } } From 76a182bb951d21d32d7672992acb7ac722f39a2c Mon Sep 17 00:00:00 2001 From: Veratil Date: Tue, 18 Aug 2020 19:45:20 -0500 Subject: [PATCH 4/5] Update portsmf to latest r234 commit --- plugins/MidiImport/portsmf/algrd_internal.h | 9 +- plugins/MidiImport/portsmf/allegro.cpp | 1254 +++++++++++---- plugins/MidiImport/portsmf/allegro.h | 351 +++-- plugins/MidiImport/portsmf/allegrord.cpp | 1527 ++++++++++--------- plugins/MidiImport/portsmf/allegrosmfrd.cpp | 900 +++++------ plugins/MidiImport/portsmf/allegrosmfwr.cpp | 62 +- plugins/MidiImport/portsmf/allegrowr.cpp | 28 +- plugins/MidiImport/portsmf/mfmidi.cpp | 33 +- plugins/MidiImport/portsmf/mfmidi.h | 20 +- plugins/MidiImport/portsmf/strparse.cpp | 12 +- plugins/MidiImport/portsmf/strparse.h | 2 +- plugins/MidiImport/portsmf/trace.cpp | 2 +- 12 files changed, 2530 insertions(+), 1670 deletions(-) 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..3c5a2a5a575 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) +#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; @@ -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, i; + Alg_track_ptr track_ptr; 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'; + 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); - char c = ser_buf.get_char(); + 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); + long len = ser_read_buf.get_int32(); + assert(ser_read_buf.get_len() >= len); + 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); + 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); + 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,7 +1837,7 @@ 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); } else { // make it match @@ -1887,17 +1983,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 +2015,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 +2057,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 +2302,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 +2311,7 @@ void Alg_time_sigs::trim(double start, double end) i_out++; } len = i_out; +#endif } @@ -2052,7 +2321,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 +2330,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 +2349,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 +2375,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 + 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; + } + // 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 +2622,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; +} + + +void Alg_tracks::set_in_use(bool flag) +{ + for (int i = 0; i < len; i++) { + tracks[i]->in_use = flag; + } } -Alg_seq::Alg_seq(const char *filename, bool smf) +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 +2790,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,8 +2864,8 @@ 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) { int ntracks = track_list.length(); @@ -2299,8 +2879,9 @@ Alg_event_ptr &Alg_seq::operator[](int i) } tr++; } - assert(false); // out of bounds + assert(false); // out of bounds } +#pragma warning(default: 4715) void Alg_seq::convert_to_beats() @@ -2369,11 +2950,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 +2981,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 +3009,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 +3040,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 +3102,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 +3181,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 +3193,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 +3263,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 +3333,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 +3346,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 +3423,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 +3490,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..7c99eb7a730 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; } @@ -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 (((long) 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); + 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); + // 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 +#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 (((long) 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 @@ -587,7 +668,8 @@ 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 +808,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 +868,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 +903,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 +1024,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 +1040,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 @@ -917,23 +1083,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..307f9562b40 100644 --- a/plugins/MidiImport/portsmf/allegrord.cpp +++ b/plugins/MidiImport/portsmf/allegrord.cpp @@ -1,753 +1,774 @@ -#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); + 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; + 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; + 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; + 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) +{ + 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); +} + + +double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; + + +double Alg_reader::parse_dur(string &field, double base) +{ + char *msg = "Duration expected"; + char *durs = "SIQHW"; + 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 { + 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) +{ + 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) +{ + char *msg = "Pitch expected"; + char *pitches = "ABCDEFG"; + 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; + if (s[pos] == '-') { + sign = -1; + 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..456fe500048 100644 --- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfrd.cpp @@ -1,445 +1,455 @@ -// 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_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_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 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..f58183173d2 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,11 +349,11 @@ 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 - int fps = 0; + int fps; if (s[0] == '2') { if (s[1] == '4') fps = 0; else if (s[1] == '5') fps = 1; @@ -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..eba292f8d4f 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 @@ -34,15 +35,16 @@ void Midifile_reader::midifile() while (ntrks-- > 0 && !midifile_error) readtrack(); } -int Midifile_reader::readmt(const char *s, int skip) +int Midifile_reader::readmt(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]; int c; - const char *errmsg = "expecting "; + char *errmsg = "expecting "; retry: while ( nread<4 ) { @@ -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,7 +414,7 @@ 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; @@ -444,7 +450,7 @@ void Midifile_reader::msginit() Msgindex = 0; } -char *Midifile_reader::msg() +unsigned char *Midifile_reader::msg() { return(Msgbuff); } @@ -464,21 +470,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..4c862029cf9 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,16 @@ class Midifile_reader { int egetc(); int msgleng(); - int readmt(const char *, int); + int readmt(char*,int); long to32bit(int,int,int,int); int to16bit(int,int); - void mferror(const char *); + void mferror(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); From 9eb787c9e81f955d041a987e94f23f728dfcdb8e Mon Sep 17 00:00:00 2001 From: Veratil Date: Sun, 23 Aug 2020 16:15:25 -0500 Subject: [PATCH 5/5] Update MIDI loading and parsing portsmf fixes: * Fix allegro warnings as errors * Fix msvc missing max. Use MAX macro instead --- plugins/MidiImport/MidiImport.cpp | 49 ++++++++++---------- plugins/MidiImport/portsmf/allegro.cpp | 40 +++++++++-------- plugins/MidiImport/portsmf/allegro.h | 33 +++++++------- plugins/MidiImport/portsmf/allegrord.cpp | 50 ++++++++++++--------- plugins/MidiImport/portsmf/allegrosmfrd.cpp | 28 +++++++----- plugins/MidiImport/portsmf/allegrosmfwr.cpp | 2 +- plugins/MidiImport/portsmf/mfmidi.cpp | 17 ++++--- plugins/MidiImport/portsmf/mfmidi.h | 3 +- 8 files changed, 123 insertions(+), 99 deletions(-) diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index f183fb418f2..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" @@ -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/allegro.cpp b/plugins/MidiImport/portsmf/allegro.cpp index 3c5a2a5a575..5664e910eca 100644 --- a/plugins/MidiImport/portsmf/allegro.cpp +++ b/plugins/MidiImport/portsmf/allegro.cpp @@ -30,7 +30,7 @@ using namespace std; // 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) +//#pragma warning(disable: 4311 4996 4267) Alg_atoms symbol_table; Serial_read_buffer Alg_track::ser_read_buf; // declare the static variables Serial_write_buffer Alg_track::ser_write_buf; @@ -720,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]; @@ -739,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, i; - Alg_track_ptr 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 && @@ -1522,7 +1522,7 @@ Alg_track *Alg_track::unserialize(void *buffer, long len) bool alg = ser_read_buf.get_char() == 'A' && ser_read_buf.get_char() == 'L' && ser_read_buf.get_char() == 'G'; - assert(alg); + assert(alg); (void)alg; // unused variable char c = ser_read_buf.get_char(); if (c == 'S') { Alg_seq *seq = new Alg_seq; @@ -1539,7 +1539,7 @@ Alg_track *Alg_track::unserialize(void *buffer, long len) } -#pragma warning(disable: 4800) // long to bool performance warning +//#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. @@ -1551,9 +1551,9 @@ void Alg_seq::unserialize_seq() (ser_read_buf.get_char() == 'L') && (ser_read_buf.get_char() == 'G') && (ser_read_buf.get_char() == 'S'); - assert(algs); + assert(algs); (void)algs; // unused variable long len = ser_read_buf.get_int32(); - assert(ser_read_buf.get_len() >= len); + 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(); @@ -1599,10 +1599,10 @@ void Alg_track::unserialize_track() (ser_read_buf.get_char() == 'L') && (ser_read_buf.get_char() == 'G') && (ser_read_buf.get_char() == 'T'); - assert(algt); + 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); + 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(); @@ -1672,7 +1672,7 @@ void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) } } -#pragma warning(default: 4800) +//#pragma warning(default: 4800) void Alg_track::set_time_map(Alg_time_map *map) { @@ -1840,6 +1840,7 @@ void Alg_track::paste(double t, Alg_event_list *seq) 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(); @@ -2381,14 +2382,14 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) 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 + /* 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; + /* 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 || @@ -2431,14 +2432,14 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) 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)); + 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)); + 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: @@ -2864,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 warning(disable: 4715) // 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; @@ -2880,8 +2881,9 @@ Alg_event_ptr &Alg_seq::operator[](int i) tr++; } assert(false); // out of bounds + return NULL; } -#pragma warning(default: 4715) +//#pragma warning(default: 4715) void Alg_seq::convert_to_beats() @@ -3044,7 +3046,7 @@ void Alg_seq::insert_silence(double t, double len) // 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)); + set_dur(t + len + MAX(get_dur() - t, 0.0)); } diff --git a/plugins/MidiImport/portsmf/allegro.h b/plugins/MidiImport/portsmf/allegro.h index 7c99eb7a730..ed684fd8a88 100644 --- a/plugins/MidiImport/portsmf/allegro.h +++ b/plugins/MidiImport/portsmf/allegro.h @@ -391,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); @@ -549,12 +549,12 @@ typedef class Serial_read_buffer : public Serial_buffer { // 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 +//#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 (((long) ptr) & 7) ptr++; } + void get_pad() { while ((intptr_t) ptr & 7) ptr++; } #if defined(_WIN32) -#pragma warning(default: 4311 546) +//#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 @@ -571,7 +571,7 @@ typedef class Serial_read_buffer : public Serial_buffer { 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); + assert(ptr < fence); (void)fence; // unused variable while (*ptr++) assert(ptr < fence); get_pad(); return s; } @@ -600,18 +600,18 @@ typedef class Serial_write_buffer: public Serial_buffer { void check_buffer(long needed); 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); // 4311 is type cast pointer to long warning // 4312 is type cast long to pointer warning #if defined(_WIN32) -#pragma warning(disable: 4311 4312) +//#pragma warning(disable: 4311 4312) #endif assert((char *)(((long) (ptr + 7)) & ~7) <= fence); #if defined(_WIN32) -#pragma warning(default: 4311 4312) +//#pragma warning(default: 4311 4312) #endif pad(); } void set_int32(long v) { *((long *) ptr) = v; ptr += 4; } @@ -619,12 +619,12 @@ typedef class Serial_write_buffer: public Serial_buffer { void set_float(float v) { *((float *) ptr) = v; ptr += 4; } void set_char(char v) { *ptr++ = v; } #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 +//#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 (((long) ptr) & 7) set_char(0); } + void pad() { while ((intptr_t) ptr & 7) set_char(0); } #if defined(_WIN32) -#pragma warning(default: 4311 546) +//#pragma warning(default: 4311 546) #endif void *to_heap(long *len) { *len = get_posn(); @@ -653,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]; } @@ -669,7 +669,8 @@ typedef class Alg_track : public Alg_event_list { Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map, bool units_are_seconds); virtual ~Alg_track() { // note: do not call set_time_map(NULL)! - if (time_map) time_map->dereference(); 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. @@ -1058,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(); diff --git a/plugins/MidiImport/portsmf/allegrord.cpp b/plugins/MidiImport/portsmf/allegrord.cpp index 307f9562b40..2dc683afd69 100644 --- a/plugins/MidiImport/portsmf/allegrord.cpp +++ b/plugins/MidiImport/portsmf/allegrord.cpp @@ -40,6 +40,7 @@ class Alg_reader { 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); @@ -116,19 +117,19 @@ Alg_parameters_ptr Alg_reader::process_attributes( if (attributes) { Alg_parameters_ptr a; bool in_seconds = seq->get_units_are_seconds(); - if (a = Alg_parameters::remove_key(&attributes, "tempor")) { + 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")) { + 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")) { + 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")) { + if ((a = Alg_parameters::remove_key(&attributes, "timesig_denr"))) { tsden = a->parm.r; ts_flag = true; } @@ -158,7 +159,7 @@ bool Alg_reader::parse() while (line_parser_flag) { bool time_flag = false; bool next_flag = false; - double next; + double next = 0; bool voice_flag = false; bool loud_flag = false; bool dur_flag = false; @@ -422,11 +423,11 @@ bool Alg_reader::parse() long Alg_reader::parse_chan(string &field) { const char *int_string = field.c_str() + 1; - char *msg = "Integer or - expected"; + 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++) { + while ((c = *p++)) { if (!isdigit(c) && c != '-') { parse_error(field, p - field.c_str() - 1, msg); return 0; @@ -449,11 +450,11 @@ long Alg_reader::parse_chan(string &field) long Alg_reader::parse_int(string &field) { const char *int_string = field.c_str() + 1; - char *msg = "Integer expected"; + const char *msg = "Integer expected"; const char *p = int_string; char c; // check that all chars in int_string are digits: - while (c = *p++) { + while ((c = *p++)) { if (!isdigit(c)) { parse_error(field, p - field.c_str() - 1, msg); return 0; @@ -491,7 +492,7 @@ int Alg_reader::find_real_in(string &field, int n) double Alg_reader::parse_real(string &field) { - char *msg = "Real expected"; + 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()) { @@ -514,15 +515,20 @@ void Alg_reader::parse_error(string &field, long offset, char *message) 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) { - char *msg = "Duration expected"; - char *durs = "SIQHW"; - char *p; + const char *msg = "Duration expected"; + const char *durs = "SIQHW"; + const char *p; int last; double dur; if (field.length() < 2) { @@ -535,7 +541,7 @@ double Alg_reader::parse_dur(string &field, double base) // 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]))) { + } else if ((p = strchr(durs, toupper(field[1])))) { dur = duration_lookup[p - durs]; last = 2; } else { @@ -578,7 +584,7 @@ double Alg_reader::parse_after_dur(double dur, string &field, } struct loud_lookup_struct { - char *str; + const char *str; int val; } loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100}, {"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60}, @@ -587,7 +593,7 @@ struct loud_lookup_struct { double Alg_reader::parse_loud(string &field) { - char *msg = "Loudness expected"; + const char *msg = "Loudness expected"; if (isdigit(field[1])) { return parse_int(field); } else { @@ -613,14 +619,14 @@ int key_lookup[] = {21, 23, 12, 14, 16, 17, 19}; // long Alg_reader::parse_key(string &field) { - char *msg = "Pitch expected"; - char *pitches = "ABCDEFG"; - char *p; + 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]))) { + } else if ((p = strchr(pitches, toupper(field[1])))) { long key = key_lookup[p - pitches]; key = parse_after_key(key, field, 2); return key; @@ -716,9 +722,9 @@ bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i) } else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') { int pos = i; bool period = false; - int sign = 1; + /* int sign = 1; LMMS unused variable */ if (s[pos] == '-') { - sign = -1; + /* sign = -1; LMMS unused variable */ pos++; } while (pos < len) { diff --git a/plugins/MidiImport/portsmf/allegrosmfrd.cpp b/plugins/MidiImport/portsmf/allegrosmfrd.cpp index 456fe500048..279462762a7 100644 --- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfrd.cpp @@ -75,6 +75,7 @@ class Alg_midifile_reader: public Midifile_reader { 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); @@ -174,14 +175,19 @@ 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 +//#pragma warning(disable: 4996) // msg is long enough sprintf(msg, "file format %d not implemented", format); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) Mf_error(msg); } divisions = division; @@ -268,9 +274,9 @@ 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 +//#pragma warning(disable: 4996) // name is long enough sprintf(name, "control%dr", control); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) parameter.set_attr(symbol_table.insert_string(name)); parameter.r = val / 127.0; update(chan, -1, ¶meter); @@ -314,9 +320,9 @@ void Alg_midifile_reader::binary_msg(int len, unsigned char *msg, 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 +//#pragma warning(disable: 4996) // hexstr is long enough sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i])); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) } parameter.s = hexstr; parameter.set_attr(symbol_table.insert_string(attr_string)); @@ -340,9 +346,9 @@ void Alg_midifile_reader::Mf_arbitrary(int len, unsigned char *msg) void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg) { char text[128]; -#pragma warning(disable: 4996) // text is long enough +//#pragma warning(disable: 4996) // text is long enough sprintf(text, "metamsic data, type 0x%x, ignored", type); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) Mf_error(text); } @@ -353,7 +359,7 @@ void Alg_midifile_reader::Mf_seqnum(int n) } -static char *fpsstr[4] = {"24", "25", "29.97", "30"}; +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) @@ -363,10 +369,10 @@ void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs, char text[32]; int fps = (hours >> 6) & 3; hours &= 0x1F; -#pragma warning(disable: 4996) // text is long enough +//#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) +//#pragma warning(default: 4996) Alg_parameter smpteoffset; smpteoffset.s = heapify(text); smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets")); diff --git a/plugins/MidiImport/portsmf/allegrosmfwr.cpp b/plugins/MidiImport/portsmf/allegrosmfwr.cpp index f58183173d2..7cac9a0412f 100644 --- a/plugins/MidiImport/portsmf/allegrosmfwr.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfwr.cpp @@ -353,7 +353,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) int len = strlen(s); char smpteoffset[5]; if (len < 24) return; // not long enough, must be bad format - int fps; + int fps = 0; if (s[0] == '2') { if (s[1] == '4') fps = 0; else if (s[1] == '5') fps = 1; diff --git a/plugins/MidiImport/portsmf/mfmidi.cpp b/plugins/MidiImport/portsmf/mfmidi.cpp index eba292f8d4f..7ea4b6ba357 100644 --- a/plugins/MidiImport/portsmf/mfmidi.cpp +++ b/plugins/MidiImport/portsmf/mfmidi.cpp @@ -35,7 +35,7 @@ void Midifile_reader::midifile() while (ntrks-- > 0 && !midifile_error) readtrack(); } -int Midifile_reader::readmt(char *s, int skip) +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. */ { @@ -44,7 +44,7 @@ int Midifile_reader::readmt(char *s, int skip) char b[4]; char buff[32]; int c; - char *errmsg = "expecting "; + const char *errmsg = "expecting "; retry: while ( nread<4 ) { @@ -68,10 +68,10 @@ int Midifile_reader::readmt(char *s, int skip) goto retry; } err: -#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths +//#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 +//#pragma warning(default: 4996) // turn it back on mferror(buff); return(0); } @@ -257,9 +257,9 @@ void Midifile_reader::readtrack() void Midifile_reader::badbyte(int c) { char buff[32]; -#pragma warning(disable: 4996) // safe in this case +//#pragma warning(disable: 4996) // safe in this case (void) sprintf(buff,"unexpected byte: 0x%02x",c); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) mferror(buff); } @@ -420,6 +420,11 @@ void Midifile_reader::mferror(char *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(). */ diff --git a/plugins/MidiImport/portsmf/mfmidi.h b/plugins/MidiImport/portsmf/mfmidi.h index 4c862029cf9..744411e3bba 100644 --- a/plugins/MidiImport/portsmf/mfmidi.h +++ b/plugins/MidiImport/portsmf/mfmidi.h @@ -81,10 +81,11 @@ class Midifile_reader { int egetc(); int msgleng(); - int readmt(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);