diff --git a/.travis.yml b/.travis.yml
index 9185ded113a..d1806bf0990 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,10 +4,6 @@ dist: trusty
sudo: required
matrix:
include:
- - env: TARGET_OS=win32
- - env: TARGET_OS=win64
- - os: osx
- osx_image: xcode8.2
- env: QT5=True
- env: QT5=True TARGET_OS=win32
- env: QT5=True TARGET_OS=win64
diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh
index 7b591feb962..da31f6ce6a0 100644
--- a/.travis/linux..install.sh
+++ b/.travis/linux..install.sh
@@ -11,7 +11,7 @@ PACKAGES="$PACKAGES libjack0"
if [ $QT5 ]; then
PACKAGES="$PACKAGES qtbase5-dev qttools5-dev-tools qttools5-dev"
else
- PACKAGES="$PACKAGES libqt4-dev"
+ PACKAGES="$PACKAGES libqt4-dev libdrumstick-dev"
fi
sudo apt-get install -y $PACKAGES
diff --git a/.travis/linux..script.sh b/.travis/linux..script.sh
index f4ab59f6f81..8ef8c513fc6 100644
--- a/.travis/linux..script.sh
+++ b/.travis/linux..script.sh
@@ -1,3 +1,7 @@
#!/usr/bin/env bash
+if [ $QT5 ];then
+ bash ${TRAVIS_BUILD_DIR}/cmake/linux/build_linux_drumstick.sh 1.1.0
+fi
+
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_WERROR=ON -DWANT_QT5=$QT5 ..
diff --git a/.travis/linux.win32.script.sh b/.travis/linux.win32.script.sh
index 3058f71d297..072916646d9 100644
--- a/.travis/linux.win32.script.sh
+++ b/.travis/linux.win32.script.sh
@@ -1,4 +1,12 @@
#!/usr/bin/env bash
export CMAKE_OPTS="-DUSE_WERROR=ON"
+
+# Just a temporarily solution.
+if [ $QT5 ];then
+ bash ${TRAVIS_BUILD_DIR}/cmake/nsis/build_mingw_drumstick.sh 1.1.0 32
+else
+ bash ${TRAVIS_BUILD_DIR}/cmake/nsis/build_mingw_drumstick.sh 0.5.0 32
+fi
+
../cmake/build_mingw32.sh
diff --git a/.travis/linux.win64.script.sh b/.travis/linux.win64.script.sh
index e7321df6a60..41390bf02ba 100644
--- a/.travis/linux.win64.script.sh
+++ b/.travis/linux.win64.script.sh
@@ -1,4 +1,11 @@
#!/usr/bin/env bash
export CMAKE_OPTS="-DUSE_WERROR=ON"
+
+
+if [ $QT5 ];then
+ bash ${TRAVIS_BUILD_DIR}/cmake/nsis/build_mingw_drumstick.sh 1.1.0 64
+else
+ bash ${TRAVIS_BUILD_DIR}/cmake/nsis/build_mingw_drumstick.sh 0.5.0 64
+fi
../cmake/build_mingw64.sh
diff --git a/.travis/osx..script.sh b/.travis/osx..script.sh
index f2d611e64d2..348619f3e64 100644
--- a/.travis/osx..script.sh
+++ b/.travis/osx..script.sh
@@ -5,4 +5,11 @@ if [ $QT5 ]; then
export CMAKE_PREFIX_PATH="$(brew --prefix qt55)"
fi
+if [ $QT5 ];then
+ bash ${TRAVIS_BUILD_DIR}/cmake/apple/build_osx_drumstick.sh 1.1.0
+else
+
+ bash ${TRAVIS_BUILD_DIR}/cmake/apple/build_osx_drumstick.sh 0.5.0
+fi
+
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWANT_QT5=$QT5 -DUSE_WERROR=OFF ..
diff --git a/cmake/apple/build_osx_drumstick.sh b/cmake/apple/build_osx_drumstick.sh
new file mode 100644
index 00000000000..0439000d85f
--- /dev/null
+++ b/cmake/apple/build_osx_drumstick.sh
@@ -0,0 +1,23 @@
+drumstick_ver=$1
+
+cd /tmp
+
+wget https://master.dl.sourceforge.net/project/drumstick/${drumstick_ver}/drumstick-${drumstick_ver}.tar.gz
+tar xf drumstick-${drumstick_ver}.tar.gz
+cd drumstick-${drumstick_ver}
+
+# We just need library
+sed -i "" "/ADD_SUBDIRECTORY(utils)/d" CMakeLists.txt
+sed -i "" "/add_subdirectory(utils)/d" CMakeLists.txt
+
+mkdir -p build && cd build
+
+if [ $QT5 ];then
+ STATIC_OPTS=ON
+else
+ STATIC_OPTS=OFF
+fi
+
+cmake ../ -DUSE_WERROR=OFF -DLIB_SUFFIX='' -DSTATIC_DRUMSTICK=$STATIC_OPTS
+make && sudo make install
+
diff --git a/cmake/linux/build_linux_drumstick.sh b/cmake/linux/build_linux_drumstick.sh
new file mode 100644
index 00000000000..b472ba6e6eb
--- /dev/null
+++ b/cmake/linux/build_linux_drumstick.sh
@@ -0,0 +1,17 @@
+drumstick_ver=$1
+
+cd /tmp
+
+wget https://master.dl.sourceforge.net/project/drumstick/${drumstick_ver}/drumstick-${drumstick_ver}.tar.gz
+tar xf drumstick-${drumstick_ver}.tar.gz
+cd drumstick-${drumstick_ver}
+
+# We just need library
+sed -i "/ADD_SUBDIRECTORY(utils)/d" CMakeLists.txt
+sed -i "/add_subdirectory(utils)/d" CMakeLists.txt
+
+mkdir -p build && cd build
+
+cmake ../ -DUSE_WERROR=OFF -DLIB_SUFFIX='' -DCMAKE_INSTALL_PREFIX=/usr
+make && sudo make install
+
diff --git a/cmake/nsis/build_mingw_drumstick.sh b/cmake/nsis/build_mingw_drumstick.sh
new file mode 100644
index 00000000000..d4d3d30be94
--- /dev/null
+++ b/cmake/nsis/build_mingw_drumstick.sh
@@ -0,0 +1,54 @@
+drumstick_ver=$1
+
+cd /tmp
+
+wget https://master.dl.sourceforge.net/project/drumstick/${drumstick_ver}/drumstick-${drumstick_ver}.tar.gz
+tar xf drumstick-${drumstick_ver}.tar.gz
+cd drumstick-${drumstick_ver}
+
+# We just need library, for utils depends on Qt5Svg
+# But no mingw*-x-qt5svg
+sed -i "/ADD_SUBDIRECTORY(utils)/d" CMakeLists.txt
+sed -i "/add_subdirectory(utils)/d" CMakeLists.txt
+
+mkdir -p build && cd build
+
+case $2 in
+ 32)
+ export MINGW=/opt/mingw32
+ export PROCESSOR=i686
+ ;;
+ 64)
+ export MINGW=/opt/mingw64
+ export PROCESSOR=x86_64
+ ;;
+ *)
+ ;;
+esac
+
+export PKG_CONFIG_PATH=${MINGW}/lib/pkgconfig
+export MINGW_TOOL_PREFIX=${MINGW}/bin/${PROCESSOR}-w64-mingw32-
+
+export CMAKE_OPTS="-DUSE_WERROR=ON
+ -DLIB_SUFFIX=''
+ -DCMAKE_INSTALL_PREFIX=$MINGW
+ -DCMAKE_PREFIX_PATH=$MINGW
+ -DMINGW_PREFIX=$MINGW
+ -DCMAKE_SYSTEM_PROCESSOR=$PROCESSOR
+ -DCMAKE_SYSTEM_NAME=Windows
+ -DCMAKE_SYSTEM_VERSION=1
+ -DCMAKE_FIND_ROOT_PATH=$MINGW
+ -DCMAKE_C_COMPILER=${MINGW_TOOL_PREFIX}gcc
+ -DCMAKE_CXX_COMPILER=${MINGW_TOOL_PREFIX}g++
+ -DCMAKE_RC_COMPILER=${MINGW_TOOL_PREFIX}gcc
+ -DSTRIP=${MINGW_TOOL_PREFIX}strip
+ -DWINDRES=${MINGW_TOOL_PREFIX}windres
+ -DPKG_CONFIG_EXECUTABLE=${MINGW_TOOL_PREFIX}pkgconfig
+ -DQT_BINARY_DIR=$MINGW/bin
+ -DQT_QMAKE_EXECUTABLE=$MINGW/bin/qmake"
+
+export PATH=$PATH:$MINGW/bin
+
+cmake ../ $CMAKE_OPTS
+make && sudo make install
+
diff --git a/data/locale/en.ts b/data/locale/en.ts
index 400ed17c996..e60a19c513d 100644
--- a/data/locale/en.ts
+++ b/data/locale/en.ts
@@ -4,91 +4,112 @@
AboutDialog
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
AmplifierControlDialog
+
+
+
+
+
+
+
+
@@ -96,18 +117,22 @@ If you're interested in translating LMMS in another language or want to imp
AmplifierControls
+
+
+
+
@@ -115,10 +140,12 @@ If you're interested in translating LMMS in another language or want to imp
AudioAlsaSetupWidget
+
+
@@ -126,78 +153,98 @@ If you're interested in translating LMMS in another language or want to imp
AudioFileProcessorView
+
+
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
@@ -205,6 +252,7 @@ If you're interested in translating LMMS in another language or want to imp
AudioFileProcessorWaveView
+
@@ -212,26 +260,35 @@ If you're interested in translating LMMS in another language or want to imp
AudioJack
+
+
+
+
+
+
+ AudioJack::setupWidget
+
+
@@ -239,10 +296,12 @@ If you're interested in translating LMMS in another language or want to imp
AudioOss::setupWidget
+
+
@@ -250,10 +309,12 @@ If you're interested in translating LMMS in another language or want to imp
AudioPortAudio::setupWidget
+
+
@@ -261,10 +322,12 @@ If you're interested in translating LMMS in another language or want to imp
AudioPulseAudio::setupWidget
+
+
@@ -272,6 +335,7 @@ If you're interested in translating LMMS in another language or want to imp
AudioSdl::setupWidget
+
@@ -279,10 +343,12 @@ If you're interested in translating LMMS in another language or want to imp
AudioSndio::setupWidget
+
+
@@ -290,10 +356,12 @@ If you're interested in translating LMMS in another language or want to imp
AudioSoundIo::setupWidget
+
+
@@ -301,61 +369,75 @@ If you're interested in translating LMMS in another language or want to imp
AutomatableModel
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
AutomationEditor
+
+
+
@@ -363,142 +445,179 @@ If you're interested in translating LMMS in another language or want to imp
AutomationEditorWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
+
+
@@ -506,6 +625,7 @@ If you're interested in translating LMMS in another language or want to imp
AutomationPattern
+
@@ -513,46 +633,57 @@ If you're interested in translating LMMS in another language or want to imp
AutomationPatternView
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
@@ -560,6 +691,7 @@ If you're interested in translating LMMS in another language or want to imp
AutomationTrack
+
@@ -567,77 +699,95 @@ If you're interested in translating LMMS in another language or want to imp
BBEditor
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
BBTCOView
+
+
+
+
+
@@ -645,10 +795,12 @@ If you're interested in translating LMMS in another language or want to imp
BBTrack
+
+
@@ -656,26 +808,32 @@ If you're interested in translating LMMS in another language or want to imp
BassBoosterControlDialog
+
+
+
+
+
+
@@ -683,14 +841,17 @@ If you're interested in translating LMMS in another language or want to imp
BassBoosterControls
+
+
+
@@ -698,82 +859,104 @@ If you're interested in translating LMMS in another language or want to imp
BitcrushControlDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -781,10 +964,12 @@ If you're interested in translating LMMS in another language or want to imp
CaptionMenu
+
+
@@ -792,10 +977,12 @@ If you're interested in translating LMMS in another language or want to imp
CarlaInstrumentView
+
+
@@ -803,6 +990,7 @@ If you're interested in translating LMMS in another language or want to imp
Controller
+
@@ -810,58 +998,73 @@ If you're interested in translating LMMS in another language or want to imp
ControllerConnectionDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -869,18 +1072,22 @@ If you're interested in translating LMMS in another language or want to imp
ControllerRackView
+
+
+
+
@@ -888,93 +1095,115 @@ If you're interested in translating LMMS in another language or want to imp
ControllerView
+
+
+
+
-
+
+
-
+
+
-
+
+
CrossoverEQControlDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -982,22 +1211,27 @@ If you're interested in translating LMMS in another language or want to imp
DelayControls
+
+
+
+
+
@@ -1005,38 +1239,48 @@ If you're interested in translating LMMS in another language or want to imp
DelayControlsDialog
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1044,185 +1288,258 @@ If you're interested in translating LMMS in another language or want to imp
DualFilterControlDialog
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
DualFilterControls
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1230,41 +1547,50 @@ If you're interested in translating LMMS in another language or want to imp
Editor
+
+
+
+
+
+
+
+
+
-
-
-
-
Effect
+
+
+
+
@@ -1272,6 +1598,7 @@ If you're interested in translating LMMS in another language or want to imp
EffectChain
+
@@ -1279,10 +1606,12 @@ If you're interested in translating LMMS in another language or want to imp
EffectRackView
+
+
@@ -1290,22 +1619,28 @@ If you're interested in translating LMMS in another language or want to imp
EffectSelectDialog
+
+
+
+
+
+
@@ -1313,54 +1648,67 @@ If you're interested in translating LMMS in another language or want to imp
EffectView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1392,58 +1743,72 @@ Right clicking will bring up a context menu where you can change the order in wh
EnvelopeAndLfoParameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1451,349 +1816,439 @@ Right clicking will bring up a context menu where you can change the order in wh
EnvelopeAndLfoView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
EqControls
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1801,82 +2256,105 @@ Right clicking will bring up a context menu where you can change the order in wh
EqControlsDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
@@ -1884,14 +2362,18 @@ Right clicking will bring up a context menu where you can change the order in wh
EqHandle
+
+
+
+
@@ -1899,167 +2381,208 @@ Right clicking will bring up a context menu where you can change the order in wh
ExportProjectDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
+
+
@@ -2067,6 +2590,8 @@ Please make sure you have write-permission to the file and the directory contain
Fader
+
+
@@ -2074,6 +2599,7 @@ Please make sure you have write-permission to the file and the directory contain
FileBrowser
+
@@ -2081,65 +2607,80 @@ Please make sure you have write-permission to the file and the directory contain
FileBrowserTreeWidget
+
-
-
-
-
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
+
+
+
+
FlangerControls
+
+
+
+
+
+
@@ -2147,46 +2688,57 @@ Please make sure you have write-permission to the file and the directory contain
FlangerControlsDialog
-
-
-
-
-
-
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2194,10 +2746,12 @@ Please make sure you have write-permission to the file and the directory contain
FxLine
+
+
+
+
+
+
+
@@ -2231,10 +2790,14 @@ You can remove and move FX channels in the context menu, which is accessed by ri
FxMixer
+
+
+
+
@@ -2242,26 +2805,35 @@ You can remove and move FX channels in the context menu, which is accessed by ri
FxMixerView
+
+
+
+ FxMixerView::FxChannelView
+
+
+
+
+
@@ -2269,6 +2841,8 @@ You can remove and move FX channels in the context menu, which is accessed by ri
FxRoute
+
+
@@ -2276,14 +2850,17 @@ You can remove and move FX channels in the context menu, which is accessed by ri
GigInstrument
+
+
+
@@ -2291,46 +2868,58 @@ You can remove and move FX channels in the context menu, which is accessed by ri
GigInstrumentView
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2338,42 +2927,52 @@ You can remove and move FX channels in the context menu, which is accessed by ri
GuiApplication
+
+
+
+
+
+
+
+
+
+
@@ -2381,650 +2980,814 @@ You can remove and move FX channels in the context menu, which is accessed by ri
InstrumentFunctionArpeggio
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ InstrumentFunctionArpeggioView
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- InstrumentFunctionArpeggioView
-
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
InstrumentFunctionNoteStacking
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
InstrumentFunctionNoteStackingView
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
InstrumentMidiIOView
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
+
+
+
@@ -3032,10 +3795,12 @@ You can remove and move FX channels in the context menu, which is accessed by ri
InstrumentMiscView
+
+
@@ -3043,126 +3808,158 @@ You can remove and move FX channels in the context menu, which is accessed by ri
InstrumentSoundShaping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3170,50 +3967,62 @@ You can remove and move FX channels in the context menu, which is accessed by ri
InstrumentSoundShapingView
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
@@ -3221,42 +4030,54 @@ You can remove and move FX channels in the context menu, which is accessed by ri
InstrumentTrack
-
+
+
+
-
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
@@ -3264,42 +4085,52 @@ You can remove and move FX channels in the context menu, which is accessed by ri
InstrumentTrackView
+
+
+
+
+
+
+
+
+
+
@@ -3307,125 +4138,156 @@ You can remove and move FX channels in the context menu, which is accessed by ri
InstrumentTrackWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
+
+
-
-
-
-
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
Knob
+
+
+
+
@@ -3433,6 +4295,7 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LadspaControl
+
@@ -3440,10 +4303,12 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LadspaControlDialog
+
+
@@ -3451,14 +4316,17 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LadspaControlView
+
+
+
@@ -3466,6 +4334,7 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LadspaEffect
+
@@ -3473,6 +4342,7 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LcdSpinBox
+
@@ -3480,18 +4350,26 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LeftRightNav
+
+
+
+
+
+
+
+
@@ -3499,30 +4377,37 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LfoController
+
+
+
+
+
+
+
@@ -3530,114 +4415,141 @@ You can remove and move FX channels in the context menu, which is accessed by ri
LfoControllerDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
LmmsCore
+
+
+
+
@@ -3645,384 +4557,495 @@ Double click to pick a file.
MainWindow
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
-
+
+
-
+
+
+
+
-
+
+
-
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4030,14 +5053,19 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS.
MeterDialog
+
+
+
+
+
@@ -4045,10 +5073,12 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS.
MeterModel
+
+
@@ -4056,41 +5086,26 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS.
MidiController
+
+
-
- MidiImport
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
MidiJack
+
When JACK(JACK Audio Connection Kit) disconnects, it will show the following message (title)
+
When JACK(JACK Audio Connection Kit) disconnects, it will show the following message (dialog message)
@@ -4099,53 +5114,65 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS.
MidiPort
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
MidiSetupWidget
+
@@ -4153,474 +5180,595 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS.
MonstroInstrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4628,20 +5776,24 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS.
MonstroView
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
-
+
+
+
+
+
-
+
+
+
-
+
+
+
+
-
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
-
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4907,34 +6210,42 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
MultitapEchoControlDialog
+
+
+
+
+
+
+
+
@@ -4942,82 +6253,102 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
NesInstrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -5025,114 +6356,155 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
NesInstrumentView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -5140,81 +6512,103 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
OscillatorObject
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
PatchesDialog
+
+
+
+
+
+
+
+
@@ -5222,46 +6616,57 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
PatmanView
+
+
+
+
+
+
+
+
+
+
+
@@ -5269,38 +6674,47 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
PatternView
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
@@ -5308,14 +6722,17 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
PeakController
+
+
+
@@ -5323,10 +6740,12 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
PeakControllerDialog
+
+
@@ -5334,306 +6753,382 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
PeakControllerEffectControlDialog
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
PeakControllerEffectControls
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
PianoRoll
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
-
+
+
+
PianoRollWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
-
-
-
-
PianoView
+
@@ -5641,19 +7136,23 @@ PM means phase modulation: Oscillator 3's phase is modulated by oscillator
Plugin
+
+
+
+
@@ -5661,14 +7160,17 @@ Reason: "%2"
PluginBrowser
+
+
+
@@ -5676,10 +7178,12 @@ Reason: "%2"
PluginFactory
+
+
@@ -5687,118 +7191,147 @@ Reason: "%2"
ProjectNotes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -5806,68 +7339,110 @@ Reason: "%2"
ProjectRenderer
+
+
+
+ QGuiApplication
+
+
+
+
+
+
QWidget
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
RenameDialog
+
@@ -5875,73 +7450,90 @@ Reason: "%2"
SampleBuffer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
SampleTCOView
+
+
+
+
+
+
@@ -5949,41 +7541,51 @@ Reason: "%2"
SampleTrack
-
+
+
-
+
+
-
+
+
+
SampleTrackView
+
+
+
+
+
+
@@ -5991,482 +7593,661 @@ Reason: "%2"
SetupDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+ SmfImport
-
+
+
+
-
+
+
-
+
+
Song
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
SongEditor
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
SongEditorWindow
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
@@ -6474,10 +8255,12 @@ Remember to also save your project manually.
SpectrumAnalyzerControlDialog
+
+
@@ -6485,14 +8268,17 @@ Remember to also save your project manually.
SpectrumAnalyzerControls
+
+
+
@@ -6500,14 +8286,17 @@ Remember to also save your project manually.
SubWindow
+
+
+
@@ -6515,6 +8304,8 @@ Remember to also save your project manually.
TabWidget
+
+
@@ -6522,74 +8313,93 @@ Remember to also save your project manually.
TempoSyncKnob
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -6597,30 +8407,37 @@ Remember to also save your project manually.
TimeDisplayWidget
+
+
+
+
+
+
+
@@ -6628,34 +8445,43 @@ Remember to also save your project manually.
TimeLineWidget
+
+
+
+
+
+
+
+
+
@@ -6663,10 +8489,12 @@ Remember to also save your project manually.
Track
+
+
@@ -6674,43 +8502,60 @@ Remember to also save your project manually.
TrackContainer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
-
+
+
-
+
+
TrackContentObject
+
@@ -6718,46 +8563,58 @@ Please make sure you have read-permission to the file and the directory containi
TrackContentObjectView
+
+
+
+
+
+
+
+
+
+
+
+
@@ -6765,193 +8622,243 @@ Please make sure you have read-permission to the file and the directory containi
TrackOperationsWidget
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
TripleOscillatorView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -6959,101 +8866,131 @@ Please make sure you have read-permission to the file and the directory containi
VersionedSaveDialog
+
+
+
+
+
+
+
VestigeInstrumentView
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
@@ -7061,10 +8998,12 @@ Please make sure you have read-permission to the file and the directory containi
VisualizationWidget
+
+
@@ -7072,54 +9011,69 @@ Please make sure you have read-permission to the file and the directory containi
VstEffectControlDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7127,173 +9081,217 @@ Please make sure you have read-permission to the file and the directory containi
VstPlugin
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
WatsynInstrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7301,213 +9299,291 @@ Please make sure you have read-permission to the file and the directory containi
WatsynView
-
+
+
+
+
+
-
+
+
+
+
+
-
+
+
+
+
+
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
ZynAddSubFxInstrument
+
+
+
+
+
+
+
+
@@ -7515,121 +9591,150 @@ Please make sure you have read-permission to the file and the directory containi
ZynAddSubFxView
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
audioFileProcessor
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
+
@@ -7637,6 +9742,7 @@ Please make sure you have read-permission to the file and the directory containi
bitInvader
+
@@ -7644,165 +9750,215 @@ Please make sure you have read-permission to the file and the directory containi
bitInvaderView
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
+
+ commonReader
-
+
+
+
+
dynProcControlDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7810,22 +9966,27 @@ Please make sure you have read-permission to the file and the directory containi
dynProcControls
+
+
+
+
+
@@ -7833,10 +9994,12 @@ Please make sure you have read-permission to the file and the directory containi
fxLineLcdSpinBox
+
+
@@ -7844,6 +10007,7 @@ Please make sure you have read-permission to the file and the directory containi
graphModel
+
@@ -7851,50 +10015,62 @@ Please make sure you have read-permission to the file and the directory containi
kickerInstrument
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7902,42 +10078,52 @@ Please make sure you have read-permission to the file and the directory containi
kickerInstrumentView
+
+
-
+
+
-
+
+
+
+
+
+
+
+
@@ -7945,26 +10131,37 @@ Please make sure you have read-permission to the file and the directory containi
ladspaBrowserView
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7988,10 +10186,12 @@ Double clicking any of the plugins will bring up information on the ports.
ladspaDescription
+
+
@@ -7999,66 +10199,83 @@ Double clicking any of the plugins will bring up information on the ports.
ladspaPortDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -8066,46 +10283,57 @@ Double clicking any of the plugins will bring up information on the ports.
lb302Synth
+
+
+
+
+
+
+
+
+
+
+
@@ -8113,122 +10341,153 @@ Double clicking any of the plugins will bring up information on the ports.
lb302SynthView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -8236,118 +10495,147 @@ Double clicking any of the plugins will bring up information on the ports.
malletsInstrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -8355,149 +10643,186 @@ Double clicking any of the plugins will bring up information on the ports.
malletsInstrumentView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
manageVSTEffectView
+
+
+
+
+
+
+
+
@@ -8505,30 +10830,39 @@ Double clicking any of the plugins will bring up information on the ports.
manageVestigeInstrumentView
+
+
+
+
+
+
+
+
+
@@ -8536,118 +10870,147 @@ Double clicking any of the plugins will bring up information on the ports.
opl2instrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -8655,18 +11018,26 @@ Double clicking any of the plugins will bring up information on the ports.
opl2instrumentView
+
+
+
+
+
+
+
+
@@ -8674,10 +11045,12 @@ Double clicking any of the plugins will bring up information on the ports.
organicInstrument
+
+
@@ -8685,50 +11058,63 @@ Double clicking any of the plugins will bring up information on the ports.
organicInstrumentView
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+
@@ -8736,265 +11122,351 @@ Double clicking any of the plugins will bring up information on the ports.
papuInstrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
papuInstrumentView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -9002,34 +11474,42 @@ Double clicking any of the plugins will bring up information on the ports.
patchesDialog
+
+
+
+
+
+
+
+
@@ -9037,238 +11517,296 @@ Double clicking any of the plugins will bring up information on the ports.
pluginBrowser
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
sf2Instrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -9276,74 +11814,92 @@ This chip was used in the Commodore 64 computer.
sf2InstrumentView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -9351,6 +11907,7 @@ This chip was used in the Commodore 64 computer.
sfxrInstrument
+
@@ -9358,26 +11915,32 @@ This chip was used in the Commodore 64 computer.
sidInstrument
+
+
+
+
+
+
@@ -9385,134 +11948,172 @@ This chip was used in the Commodore 64 computer.
sidInstrumentView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -9520,10 +12121,12 @@ This chip was used in the Commodore 64 computer.
stereoEnhancerControlDialog
+
+
@@ -9531,6 +12134,7 @@ This chip was used in the Commodore 64 computer.
stereoEnhancerControls
+
@@ -9538,18 +12142,22 @@ This chip was used in the Commodore 64 computer.
stereoMatrixControlDialog
+
+
+
+
@@ -9557,18 +12165,22 @@ This chip was used in the Commodore 64 computer.
stereoMatrixControls
+
+
+
+
@@ -9576,10 +12188,12 @@ This chip was used in the Commodore 64 computer.
vestigeInstrument
+
+
@@ -9587,42 +12201,52 @@ This chip was used in the Commodore 64 computer.
vibed
+
+
+
+
+
+
+
+
+
+
@@ -9630,90 +12254,112 @@ This chip was used in the Commodore 64 computer.
vibedView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
voiceObject
+
+
+
+
+
+
+
+
+
+
+
@@ -9868,58 +12546,72 @@ The LED in the lower right corner of the waveform editor determines whether the
waveShaperControlDialog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -9927,10 +12619,12 @@ The LED in the lower right corner of the waveform editor determines whether the
waveShaperControls
+
+
diff --git a/include/SmfMidiCC.h b/include/SmfMidiCC.h
new file mode 100644
index 00000000000..6f8bc398c24
--- /dev/null
+++ b/include/SmfMidiCC.h
@@ -0,0 +1,51 @@
+/*
+ * smfMidiCC.h - support for importing MIDI files
+ *
+ * Copyright (c) 2005-2014 Tobias Doerffel
+ *
+ * This file is part of LMMS - http://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 SMF_MIDI_CC_H
+#define SMF_MIDI_CC_H
+
+#include
+
+#include "export.h"
+#include "TrackContainer.h"
+#include "AutomationTrack.h"
+#include "AutomationPattern.h"
+#include "MidiTime.h"
+
+class EXPORT SmfMidiCC
+{
+
+public:
+ SmfMidiCC();
+
+ AutomationTrack * at;
+ AutomationPattern * ap;
+ MidiTime lastPos;
+
+ SmfMidiCC & create(TrackContainer* tc, QString tn );
+ SmfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value );
+ void clear();
+};
+
+#endif
diff --git a/include/SmfMidiChannel.h b/include/SmfMidiChannel.h
new file mode 100644
index 00000000000..bdf39538272
--- /dev/null
+++ b/include/SmfMidiChannel.h
@@ -0,0 +1,57 @@
+/*
+ * smfMidiChannel.h - support for importing MIDI files
+ *
+ * Copyright (c) 2005-2014 Tobias Doerffel
+ *
+ * This file is part of LMMS - http://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 SMF_MIDI_CHANNEL_H
+#define SMF_MIDI_CHANNEL_H
+
+#include
+
+#include "export.h"
+#include "InstrumentTrack.h"
+#include "Pattern.h"
+#include "Instrument.h"
+#include "MidiTime.h"
+
+class EXPORT SmfMidiChannel
+{
+
+public:
+ SmfMidiChannel();
+
+ InstrumentTrack * it;
+ Pattern* p;
+ Instrument * it_inst;
+ bool isSF2;
+ bool hasNotes;
+ MidiTime lastEnd;
+ QString trackName;
+
+ SmfMidiChannel * create( TrackContainer* tc, QString tn );
+
+ void addNote( Note & n );
+ void setName(QString tn );
+
+};
+
+#endif
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 943c8088c59..7bfcd86aebb 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -55,8 +55,8 @@ IF("${PLUGIN_LIST}" STREQUAL "")
ladspa_browser
LadspaEffect
lb302
- MidiImport
- # MidiExport - temporarily disabled, MIDI export is broken
+ SmfImport # For Midi, Overture and Cakewalk files.
+ MidiExport # temporarily disabled, MIDI export is broken
MultitapEcho
monstro
nes
diff --git a/plugins/MidiExport/CMakeLists.txt b/plugins/MidiExport/CMakeLists.txt
index 1d19f081e6a..d2058c1db5a 100644
--- a/plugins/MidiExport/CMakeLists.txt
+++ b/plugins/MidiExport/CMakeLists.txt
@@ -1,4 +1,32 @@
-INCLUDE(BuildPlugin)
+# check for drumstick-file
-BUILD_PLUGIN(midiexport MidiExport.cpp MidiExport.h MidiFile.hpp
- MOCFILES MidiExport.h)
+IF(LMMS_HAVE_FLUIDSYNTH)
+ IF(WANT_QT5)
+ PKG_CHECK_MODULES(DRUMSTICK_FILE REQUIRED drumstick-file>=1.0.0)
+ ELSE()
+ PKG_CHECK_MODULES(DRUMSTICK_FILE REQUIRED drumstick-file>=0.5.0)
+ ENDIF()
+ IF(NOT DRUMSTICK_FILE_FOUND)
+ IF(WANT_QT5)
+ MESSAGE(FATAL_ERROR "LMMS requires libdrumstick-file and libdrumstick-dev >= 1.0.0 with Qt5 - please install, remove CMakeCache.txt and try again!")
+ ELSE()
+ MESSAGE(FATAL_ERROR "LMMS requires libdrumstick-file and libdrumstick-dev >= 0.5.0 with Qt4 - please install, remove CMakeCache.txt and try again!")
+ ENDIF()
+ ENDIF(NOT DRUMSTICK_FILE_FOUND)
+ INCLUDE(BuildPlugin)
+ LINK_DIRECTORIES(${DRUMSTICK_FILE_LIBDIR})
+ INCLUDE_DIRECTORIES(${DRUMSTICK_FILE_INCLUDEDIR})
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
+ BUILD_PLUGIN(midiexport MidiExport.cpp midiWriter.cpp
+ MOCFILES MidiExport.h midiWriter.h)
+
+ IF(NOT LMMS_BUILD_LINUX)
+ IF(WANT_QT5)
+ TARGET_LINK_LIBRARIES(midiexport ${DRUMSTICK_FILE_LIBRARIES} Qt5::Core)
+ ELSE()
+ TARGET_LINK_LIBRARIES(midiexport ${DRUMSTICK_FILE_LIBRARIES} -lQtCore4)
+ ENDIF()
+ ELSE()
+ TARGET_LINK_LIBRARIES(midiexport ${DRUMSTICK_FILE_LIBRARIES})
+ ENDIF()
+ENDIF(LMMS_HAVE_FLUIDSYNTH)
diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp
index 4cb0b356b88..7f0e6cd2966 100644
--- a/plugins/MidiExport/MidiExport.cpp
+++ b/plugins/MidiExport/MidiExport.cpp
@@ -28,8 +28,10 @@
#include
#include
#include
+#include
#include "MidiExport.h"
+#include "midiWriter.h"
#include "Engine.h"
#include "TrackContainer.h"
#include "InstrumentTrack.h"
@@ -70,98 +72,12 @@ MidiExport::~MidiExport()
bool MidiExport::tryExport( const TrackContainer::TrackList &tracks, int tempo, const QString &filename )
{
- QFile f(filename);
- f.open(QIODevice::WriteOnly);
- QDataStream midiout(&f);
-
- InstrumentTrack* instTrack;
- QDomElement element;
-
-
- int nTracks = 0;
- const int BUFFER_SIZE = 50*1024;
- uint8_t buffer[BUFFER_SIZE];
- uint32_t size;
-
- for( const Track* track : tracks ) if( track->type() == Track::InstrumentTrack ) nTracks++;
-
- // midi header
- MidiFile::MIDIHeader header(nTracks);
- size = header.writeToBuffer(buffer);
- midiout.writeRawData((char *)buffer, size);
-
- // midi tracks
- for( Track* track : tracks )
- {
- DataFile dataFile( DataFile::SongProject );
- MidiFile::MIDITrack mtrack;
-
- if( track->type() != Track::InstrumentTrack ) continue;
-
- //qDebug() << "exporting " << track->name();
-
-
- mtrack.addName(track->name().toStdString(), 0);
- //mtrack.addProgramChange(0, 0);
- mtrack.addTempo(tempo, 0);
-
- instTrack = dynamic_cast( track );
- element = instTrack->saveState( dataFile, dataFile.content() );
-
- // instrumentTrack
- // - instrumentTrack
- // - pattern
- int base_pitch = 0;
- double base_volume = 1.0;
- int base_time = 0;
-
-
- for(QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling())
- {
- //QDomText txt = n.toText();
- //qDebug() << ">> child node " << n.nodeName();
-
- if (n.nodeName() == "instrumenttrack")
- {
- // TODO interpret pan="0" fxch="0" usemasterpitch="1" pitchrange="1" pitch="0" basenote="57"
- QDomElement it = n.toElement();
- base_pitch = it.attribute("pitch", "0").toInt();
- base_volume = it.attribute("volume", "100").toDouble()/100.0;
- }
-
- if (n.nodeName() == "pattern")
- {
- base_time = n.toElement().attribute("pos", "0").toInt();
- // TODO interpret steps="12" muted="0" type="1" name="Piano1" len="2592"
- for(QDomNode nn = n.firstChild(); !nn.isNull(); nn = nn.nextSibling())
- {
- QDomElement note = nn.toElement();
- if (note.attribute("len", "0") == "0" || note.attribute("vol", "0") == "0") continue;
- #if 0
- qDebug() << ">>>> key " << note.attribute( "key", "0" )
- << " " << note.attribute("len", "0") << " @"
- << note.attribute("pos", "0");
- #endif
- mtrack.addNote(
- note.attribute("key", "0").toInt()+base_pitch
- , 100 * base_volume * (note.attribute("vol", "100").toDouble()/100)
- , (base_time+note.attribute("pos", "0").toDouble())/48
- , (note.attribute("len", "0")).toDouble()/48);
- }
- }
-
- }
- size = mtrack.writeToBuffer(buffer);
- midiout.writeRawData((char *)buffer, size);
- } // for each track
-
+ midiWriter s_mr( tracks );
+ s_mr.writeFile( filename );
return true;
-
}
-
-
void MidiExport::error()
{
//qDebug() << "MidiExport error: " << m_error ;
diff --git a/plugins/MidiExport/MidiExport.h b/plugins/MidiExport/MidiExport.h
index d829a8b8ff3..cb7b1aa4fb2 100644
--- a/plugins/MidiExport/MidiExport.h
+++ b/plugins/MidiExport/MidiExport.h
@@ -28,13 +28,11 @@
#include
#include "ExportFilter.h"
-#include "MidiFile.hpp"
-
class MidiExport: public ExportFilter
{
-// Q_OBJECT
+ Q_OBJECT
public:
MidiExport( );
~MidiExport();
@@ -47,12 +45,8 @@ class MidiExport: public ExportFilter
virtual bool tryExport( const TrackContainer::TrackList &tracks, int tempo, const QString &filename );
private:
-
-
void error( void );
-
-
-} ;
+};
#endif
diff --git a/plugins/MidiExport/MidiFile.hpp b/plugins/MidiExport/MidiFile.hpp
deleted file mode 100644
index 0e2bfbe5bd7..00000000000
--- a/plugins/MidiExport/MidiFile.hpp
+++ /dev/null
@@ -1,308 +0,0 @@
-#ifndef MIDIFILE_HPP
-#define MIDIFILE_HPP
-
-/**
- * Name: MidiFile.hpp
- * Purpose: C++ re-write of the python module MidiFile.py
- * Author: Mohamed Abdel Maksoud
- *-----------------------------------------------------------------------------
- * Name: MidiFile.py
- * Purpose: MIDI file manipulation utilities
- *
- * Author: Mark Conway Wirt
- *
- * Created: 2008/04/17
- * Copyright: (c) 2009 Mark Conway Wirt
- * License: Please see License.txt for the terms under which this
- * software is distributed.
- *-----------------------------------------------------------------------------
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using std::string;
-using std::vector;
-using std::set;
-
-namespace MidiFile
-{
-
-const int TICKSPERBEAT = 128;
-
-
-int writeVarLength(uint32_t val, uint8_t *buffer)
-{
- /*
- Accept an input, and write a MIDI-compatible variable length stream
-
- The MIDI format is a little strange, and makes use of so-called variable
- length quantities. These quantities are a stream of bytes. If the most
- significant bit is 1, then more bytes follow. If it is zero, then the
- byte in question is the last in the stream
- */
- int size = 0;
- uint8_t result, little_endian[4];
- result = val & 0x7F;
- little_endian[size++] = result;
- val = val >> 7;
- while (val > 0)
- {
- result = val & 0x7F;
- result = result | 0x80;
- little_endian[size++] = result;
- val = val >> 7;
- }
- for (int i=0; i> 24;
- buf[1] = val >> 16 & 0xff;
- buf[2] = val >> 8 & 0xff;
- buf[3] = val & 0xff;
- return 4;
-}
-
-int writeBigEndian2(uint16_t val, uint8_t *buf)
-{
- buf[0] = val >> 8 & 0xff;
- buf[1] = val & 0xff;
- return 2;
-}
-
-
-class MIDIHeader
-{
- // Class to encapsulate the MIDI header structure.
- uint16_t numTracks;
- uint16_t ticksPerBeat;
-
- public:
-
- MIDIHeader(uint16_t nTracks, uint16_t ticksPB=TICKSPERBEAT): numTracks(nTracks), ticksPerBeat(ticksPB) {}
-
- inline int writeToBuffer(uint8_t *buffer, int start=0) const
- {
- // chunk ID
- buffer[start++] = 'M'; buffer[start++] = 'T'; buffer[start++] = 'h'; buffer[start++] = 'd';
- // chunk size (6 bytes always)
- buffer[start++] = 0; buffer[start++] = 0; buffer[start++] = 0; buffer[start++] = 0x06;
- // format: 1 (multitrack)
- buffer[start++] = 0; buffer[start++] = 0x01;
-
- start += writeBigEndian2(numTracks, buffer+start);
-
- start += writeBigEndian2(ticksPerBeat, buffer+start);
-
- return start;
- }
-
-};
-
-
-struct Event
-{
- uint32_t time;
- uint32_t tempo;
- string trackName;
- enum {NOTE_ON, NOTE_OFF, TEMPO, PROG_CHANGE, TRACK_NAME} type;
- // TODO make a union to save up space
- uint8_t pitch;
- uint8_t programNumber;
- uint8_t duration;
- uint8_t volume;
- uint8_t channel;
-
- Event() {time=tempo=pitch=programNumber=duration=volume=channel=0; trackName="";}
-
- inline int writeToBuffer(uint8_t *buffer) const
- {
- uint8_t code, fourbytes[4];
- int size=0;
- switch (type)
- {
- case NOTE_ON:
- code = 0x9 << 4 | channel;
- size += writeVarLength(time, buffer+size);
- buffer[size++] = code;
- buffer[size++] = pitch;
- buffer[size++] = volume;
- break;
- case NOTE_OFF:
- code = 0x8 << 4 | channel;
- size += writeVarLength(time, buffer+size);
- buffer[size++] = code;
- buffer[size++] = pitch;
- buffer[size++] = volume;
- break;
- case TEMPO:
- code = 0xFF;
- size += writeVarLength(time, buffer+size);
- buffer[size++] = code;
- buffer[size++] = 0x51;
- buffer[size++] = 0x03;
- writeBigEndian4(int(60000000.0 / tempo), fourbytes);
-
- //printf("tempo of %x translates to ", tempo);
- for (int i=0; i<3; i++) printf("%02x ", fourbytes[i+1]);
- printf("\n");
- buffer[size++] = fourbytes[1];
- buffer[size++] = fourbytes[2];
- buffer[size++] = fourbytes[3];
- break;
- case PROG_CHANGE:
- code = 0xC << 4 | channel;
- size += writeVarLength(time, buffer+size);
- buffer[size++] = code;
- buffer[size++] = programNumber;
- break;
- case TRACK_NAME:
- size += writeVarLength(time, buffer+size);
- buffer[size++] = 0xFF;
- buffer[size++] = 0x03;
- size += writeVarLength(trackName.size(), buffer+size);
- trackName.copy((char *)(&buffer[size]), trackName.size());
- size += trackName.size();
-// buffer[size++] = '\0';
-// buffer[size++] = '\0';
-
- break;
- }
- return size;
- } // writeEventsToBuffer
-
-
- // events are sorted by their time
- inline bool operator < (const Event& b) const {
- return this->time < b.time;
- }
-};
-
-template
-class MIDITrack
-{
- // A class that encapsulates a MIDI track
- // Nested class definitions.
- vector events;
-
- public:
- uint8_t channel;
-
- MIDITrack(): channel(0) {}
-
- inline void addEvent(const Event &e)
- {
- Event E = e;
- events.push_back(E);
- }
-
- inline void addNote(uint8_t pitch, uint8_t volume, double time, double duration)
- {
- Event event; event.channel = channel;
- event.volume = volume;
-
- event.type = Event::NOTE_ON; event.pitch = pitch; event.time= (uint32_t) (time * TICKSPERBEAT);
- addEvent(event);
-
- event.type = Event::NOTE_OFF; event.pitch = pitch; event.time=(uint32_t) ((time+duration) * TICKSPERBEAT);
- addEvent(event);
-
- //printf("note: %d-%d\n", (uint32_t) time * TICKSPERBEAT, (uint32_t)((time+duration) * TICKSPERBEAT));
- }
-
- inline void addName(const string &name, uint32_t time)
- {
- Event event; event.channel = channel;
- event.type = Event::TRACK_NAME; event.time=time; event.trackName = name;
- addEvent(event);
- }
-
- inline void addProgramChange(uint8_t prog, uint32_t time)
- {
- Event event; event.channel = channel;
- event.type = Event::PROG_CHANGE; event.time=time; event.programNumber = prog;
- addEvent(event);
- }
-
- inline void addTempo(uint8_t tempo, uint32_t time)
- {
- Event event; event.channel = channel;
- event.type = Event::TEMPO; event.time=time; event.tempo = tempo;
- addEvent(event);
- }
-
- inline int writeMIDIToBuffer(uint8_t *buffer, int start=0) const
- {
- // Write the meta data and note data to the packed MIDI stream.
- // Process the events in the eventList
-
- start += writeEventsToBuffer(buffer, start);
-
- // Write MIDI close event.
- buffer[start++] = 0x00;
- buffer[start++] = 0xFF;
- buffer[start++] = 0x2F;
- buffer[start++] = 0x00;
-
- // return the entire length of the data and write to the header
-
- return start;
- }
-
- inline int writeEventsToBuffer(uint8_t *buffer, int start=0) const
- {
- // Write the events in MIDIEvents to the MIDI stream.
- vector _events = events;
- std::sort(_events.begin(), _events.end());
- vector::const_iterator it;
- uint32_t time_last = 0, tmp;
- for (it = _events.begin(); it!=_events.end(); ++it)
- {
- Event e = *it;
- if (e.time < time_last){
- printf("error: e.time=%d time_last=%d\n", e.time, time_last);
- assert(false);
- }
- tmp = e.time;
- e.time -= time_last;
- time_last = tmp;
- start += e.writeToBuffer(buffer+start);
- if (start >= MAX_TRACK_SIZE) {
- break;
- }
- }
- return start;
- }
-
- inline int writeToBuffer(uint8_t *buffer, int start=0) const
- {
- uint8_t eventsBuffer[MAX_TRACK_SIZE];
- uint32_t events_size = writeMIDIToBuffer(eventsBuffer);
- //printf(">> track %lu events took 0x%x bytes\n", events.size(), events_size);
-
- // chunk ID
- buffer[start++] = 'M'; buffer[start++] = 'T'; buffer[start++] = 'r'; buffer[start++] = 'k';
- // chunk size
- start += writeBigEndian4(events_size, buffer+start);
- // copy events data
- memmove(buffer+start, eventsBuffer, events_size);
- start += events_size;
- return start;
- }
-};
-
-}; // namespace
-
-#endif
diff --git a/plugins/MidiExport/midiWriter.cpp b/plugins/MidiExport/midiWriter.cpp
new file mode 100644
index 00000000000..b8f9f6f0dbd
--- /dev/null
+++ b/plugins/MidiExport/midiWriter.cpp
@@ -0,0 +1,538 @@
+/*
+ * midiWriter.cpp - export backend.
+ *
+ * Copyright (c) 2016-2017 Tony Chyi
+ *
+ * This file is part of LMMS - http://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.
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "AutomationTrack.h"
+#include "Engine.h"
+#include "Instrument.h"
+#include "InstrumentTrack.h"
+#include "Midi.h"
+#include "MidiEvent.h"
+#include "MidiTime.h"
+#include "Note.h"
+#include "Pattern.h"
+#include "Song.h"
+#include "Track.h"
+
+#include "midiWriter.h"
+
+#define SEARCH_AUTOTRACKS_START( automodel ) \
+ for( int autoTrackIndex : AutomationTracks ) \
+ { \
+ AutomationTrack * autoTrack = dynamic_cast< AutomationTrack * >( m_tl[ autoTrackIndex ] ); \
+ for( TrackContentObject * tco : autoTrack->getTCOs() ) \
+ { \
+ tick_t offset = tco->startPosition().getTicks();\
+ AutomationPattern * a_tco = dynamic_cast< AutomationPattern * >( tco ); \
+ if( a_tco->firstObject() != automodel ) \
+ continue; \
+ const AutomationPattern::timeMap tp = a_tco->getTimeMap(); \
+ AutomationPattern::timeMap::const_iterator i; \
+ for( i=tp.constBegin(); i!=tp.constEnd(); ++i ) \
+ {\
+
+#define SEARCH_AUTOTRACKS_END \
+ }\
+ }\
+ }
+
+#define PROGRESSION_CALC_START \
+ if( i != tp.constBegin() && \
+ a_tco->progressionType() != AutomationPattern::DiscreteProgression &&\
+ i.value() != (i-1).value() )\
+ {\
+ for( int ii = 1; ii < i.key() - (i-1).key(); ii++ ) \
+ {\
+
+#define PROGRESSION_CALC_END \
+ }\
+ }\
+
+
+#define INSERT_CC_EVENT( type, zoom, t, offset, val ) \
+ event = new MidiEvent( MidiControlChange );\
+ event->setChannel( currentChannel );\
+ event->setControllerNumber( type );\
+ event->setControllerValue( val * zoom );\
+ EventList.insert( t+offset, event );
+
+#define INSERT_PITCH_EVENT( t, offset, val ) \
+ event = new MidiEvent( MidiPitchBend );\
+ event->setChannel( currentChannel );\
+ event->setPitchBend( val / pitchBendMultiply * ( MidiMaxPitchBend / 2 ) / 100 + ( MidiMaxPitchBend / 2 + 1 ) );\
+ EventList.insert( t+offset, event );
+
+#define INSERT_PROG_EVENT( t, offset, val ) \
+ event = new MidiEvent( MidiProgramChange ); \
+ event->setChannel( currentChannel );\
+ event->setParam( 0, val ); \
+ EventList.insert( t+offset, event );
+
+#define INSERT_TEMPO_EVENT( t, val ) \
+ MidiEvent * event = new MidiEvent( MidiMetaEvent ); \
+ event->setMetaEvent( MidiSetTempo ); \
+ event->setParam( 0, val ); \
+ EventList.insert( t, event ); \
+
+#define FIND_PITCH_MULTYPLY( t ) \
+ QMap< int, int >::const_iterator j;\
+ for( j=PitchBendMultiply.constBegin(); j!=PitchBendMultiply.constEnd(); ++j )\
+ {\
+ if( j.key() <= t )\
+ pitchBendMultiply = j.value();\
+ else\
+ break;\
+ }
+
+
+
+midiWriter::midiWriter( const TrackContainer::TrackList &tracks ):
+ m_tl( tracks ), m_seq( new drumstick::QSmf( this ) ),
+ flagRpnPitchBendRangeSent( false )
+{
+ tempoPat = Engine::getSong()->tempoAutomationPattern();
+
+ MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel();
+ timeSigNumPat = AutomationPattern::globalAutomationPattern(
+ &timeSigMM.numeratorModel() );
+ timeSigDenPat = AutomationPattern::globalAutomationPattern(
+ &timeSigMM.denominatorModel() );
+
+ for( int i=0; i < MidiChannelCount; i++)
+ ChannelProg[i] = -1;
+
+ // We need multi-track format, so set format 1.
+ m_seq->setFileFormat( 1 );
+
+ int trackIndex = 0;
+ for( const Track* track : tracks )
+ {
+ switch( track->type() )
+ {
+ case Track::InstrumentTrack:
+ if( dynamic_cast< const InstrumentTrack * >( track )->instrumentName() == "Sf2 Player" )
+ InstrumentTracks << trackIndex;
+ break;
+ case Track::AutomationTrack:
+ AutomationTracks << trackIndex;
+ break;
+ default:
+ break;
+ }
+ trackIndex++;
+ }
+
+ // Track 0 is to write global events.
+ m_seq->setTracks( InstrumentTracks.size()+1 );
+ // 1 quarter note = 960 midi ticks.
+ m_seq->setDivision( DefaultMidiDivision );
+
+ tickRate = (double)DefaultTicksPerTact / 4.f / DefaultMidiDivision ;
+
+ connect( m_seq, SIGNAL( signalSMFWriteTrack(int) ),
+ this, SLOT( writeTrackEvent(int)) );
+
+}
+
+
+midiWriter::~midiWriter()
+{
+ printf( "destroy midiWriter\n" );
+ delete m_seq;
+}
+
+void midiWriter::writeFile( const QString &fileName )
+{
+
+ m_seq->writeToFile( fileName );
+}
+
+void midiWriter::writeEventToFile()
+{
+ int lastTime = 0;
+ QMultiMap< long, MidiEvent * >::const_iterator i;
+
+ for( i=EventList.constBegin(); i!=EventList.constEnd(); ++i )
+ {
+ MidiEvent *event = i.value();
+ int deltaTime = i.key() - lastTime;
+ double realDeltaTime = deltaTime / tickRate;
+
+ switch( event->type() )
+ {
+ case MidiMetaEvent:
+ switch( event->metaEvent() )
+ {
+ case MidiTimeSignature:
+ m_seq->writeTimeSignature( (long) realDeltaTime, event->param( 0 ),
+ intSqrt( event->param( 1 ) ),
+ DefaultMidiClockPerMetronomeClick * 4 /
+ event->param(1), 8 );
+
+ break;
+
+ case MidiSetTempo:
+ m_seq->writeBpmTempo( (int) realDeltaTime, event->param( 0 ) );
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case MidiControlChange:
+ switch( event->controllerNumber() )
+ {
+ case MidiControllerPan:
+ m_seq->writeMidiEvent( (long) realDeltaTime, control_change,
+ event->channel(),
+ MidiControllerPan,
+ event->controllerValue() - 127 );
+
+ break;
+ case MidiControllerDataEntry:
+ if( !flagRpnPitchBendRangeSent && event->controllerValue() > 2 )
+ {
+ m_seq->writeMidiEvent( (long) realDeltaTime, control_change,
+ event->channel(),
+ MidiControllerRegisteredParameterNumberMSB,
+ 0 );
+ m_seq->writeMidiEvent( 0, control_change,
+ event->channel(),
+ MidiControllerRegisteredParameterNumberLSB,
+ MidiPitchBendSensitivityRPN );
+ m_seq->writeMidiEvent( 0, control_change,
+ event->channel(),
+ MidiControllerDataEntry,
+ event->controllerValue() );
+ m_seq->writeMidiEvent( 0, control_change,
+ event->channel(),
+ 38, 0);
+
+ flagRpnPitchBendRangeSent = true;
+ }
+ else if( event->controllerValue() <= 2 )
+ break;
+ default:
+ m_seq->writeMidiEvent( (long) realDeltaTime, control_change,
+ event->channel(),
+ event->controllerNumber(),
+ event->controllerValue() );
+
+ }
+ break;
+
+ case MidiPitchBend:
+ m_seq->writeMidiEvent( (long) realDeltaTime, pitch_wheel,
+ event->channel(),
+ event->pitchBend() & 127,
+ (event->pitchBend() & 32512) >> 7 );
+ break;
+
+ case MidiProgramChange:
+ m_seq->writeMidiEvent( (long) realDeltaTime, program_chng,
+ event->channel(), event->program() );
+ break;
+
+ case MidiNoteOff:
+ case MidiNoteOn:
+ m_seq->writeMidiEvent( (long) realDeltaTime, event->type(),
+ event->channel(), event->param(0),
+ event->param(1) );
+ break;
+
+ default:
+ break;
+ }
+ lastTime = i.key();
+
+ // Should release memory use.
+ delete event;
+ }
+}
+
+void midiWriter::insertTempoEvent()
+{
+ AutomationPattern::timeMap time_map = tempoPat->getTimeMap();
+ AutomationPattern::timeMap::const_iterator i;
+ AutomationPattern::timeMap::const_iterator last = time_map.constBegin() ;
+
+ for( i = time_map.constBegin(); i != time_map.constEnd(); ++i )
+ {
+ if( i != time_map.constBegin() &&
+ tempoPat->progressionType() != AutomationPattern::DiscreteProgression &&
+ i.value() != last.value() )
+ {
+ for(int ii = 1; ii < i.key() - (i-1).key(); ii++ )
+ {
+ INSERT_TEMPO_EVENT( (i-1).key()+ii, tempoPat->valueAt( MidiTime( (i-1).key()+ii ) ) );
+ }
+ }
+ INSERT_TEMPO_EVENT( i.key(), i.value() );
+ last = i;
+ }
+}
+
+void midiWriter::insertTimeSigEvent()
+{
+ // In LMMS, time sig will not always appear at the same time.
+ AutomationPattern::timeMap & timeSigNum_map = timeSigNumPat->getTimeMap();
+ AutomationPattern::timeMap & timeSigDen_map = timeSigDenPat->getTimeMap();
+ AutomationPattern::timeMap::const_iterator i;
+
+ QList TimeList;
+
+ // Step 1, fetch time point from Num and Den.
+ for( i=timeSigNum_map.constBegin(); i!=timeSigNum_map.constEnd(); ++i )
+ TimeList << i.key();
+ for( i=timeSigDen_map.constBegin(); i!=timeSigDen_map.constEnd(); ++i )
+ TimeList << i.key();
+
+ // Step 2, unique and sort.
+ TimeList = TimeList.toSet().toList();
+ sort(TimeList.begin(), TimeList.end());
+
+ // Step 3, insert event.
+ uint16_t num=4, den=2;
+
+ for( int i : TimeList )
+ {
+ if( timeSigDen_map.contains(i) )
+ num = (uint16_t) timeSigDen_map[i];
+ if( timeSigNum_map.contains(i) )
+ den = (uint16_t) timeSigNum_map[i];
+
+ MidiEvent * event = new MidiEvent( MidiMetaEvent );
+ event->setMetaEvent( MidiTimeSignature );
+ /*
+ * Param 0 => Numerator
+ * Param 1 => Denominator
+ */
+ event->setParam( 0, num );
+ event->setParam( 1, den );
+ EventList.insert( i, event );
+ }
+}
+
+void midiWriter::insertCCEvent( InstrumentTrack *track )
+{
+ MidiEvent * event;
+
+ INSERT_CC_EVENT( MidiControllerPan, 127 / 100 + 127, 0, 0, track->panningModel()->value() );
+ SEARCH_AUTOTRACKS_START( track->panningModel() );
+ PROGRESSION_CALC_START;
+ INSERT_CC_EVENT( MidiControllerPan, 127 / 100 + 127, (i-1).key(), offset, a_tco->valueAt( MidiTime( (i-1).key()+ii+offset ) ) );
+ PROGRESSION_CALC_END;
+ INSERT_CC_EVENT( MidiControllerPan, 127 / 100 + 127, i.key(), offset, i.value() );
+ SEARCH_AUTOTRACKS_END;
+
+ INSERT_CC_EVENT( MidiControllerMainVolume, 127 / 200, 0, 0, track->volumeModel()->value() );
+ SEARCH_AUTOTRACKS_START( track->volumeModel() );
+ PROGRESSION_CALC_START;
+ INSERT_CC_EVENT( MidiControllerMainVolume, 127 / 200, (i-1).key(), offset, a_tco->valueAt( MidiTime( (i-1).key()+ii+offset ) ) );
+ PROGRESSION_CALC_END;
+ INSERT_CC_EVENT( MidiControllerMainVolume, 127 / 200, i.key(), offset, i.value() );
+ SEARCH_AUTOTRACKS_END;
+
+ PitchBendMultiply.insert( 0, track->pitchRangeModel()->value() );
+ INSERT_CC_EVENT( MidiControllerDataEntry, 1, 0, 0, track->pitchRangeModel()->value() );
+ SEARCH_AUTOTRACKS_START( track->pitchRangeModel() );
+ PROGRESSION_CALC_START;
+ PitchBendMultiply.insert( (i-1).key()+ii+offset, a_tco->valueAt( MidiTime( (i-1).key()+ii ) ) );
+ INSERT_CC_EVENT( MidiControllerDataEntry, 1, (i-1).key(), offset, a_tco->valueAt( MidiTime( (i-1).key()+ii+offset ) ) );
+ PROGRESSION_CALC_END;
+ PitchBendMultiply.insert( i.key()+offset, i.value() );
+ INSERT_CC_EVENT( MidiControllerDataEntry, 1, i.key(), offset, i.value() );
+ SEARCH_AUTOTRACKS_END;
+
+ // DrumChannel(10) no need bank select.
+ if( currentChannel == DrumChannel )
+ return;
+ INSERT_CC_EVENT( MidiControllerBankSelect, 1, 0, 0, dynamic_cast< IntModel * >( track->instrument()->childModel( "bank" ) )->value() );
+ SEARCH_AUTOTRACKS_START( track->instrument()->childModel( "bank" ) );
+ PROGRESSION_CALC_START;
+ INSERT_CC_EVENT( MidiControllerBankSelect, 1, (i-1).key(), offset, a_tco->valueAt( MidiTime( (i-1).key()+ii+offset ) ) );
+ PROGRESSION_CALC_END;
+ INSERT_CC_EVENT( MidiControllerBankSelect, 1, i.key(), offset, i.value() );
+ SEARCH_AUTOTRACKS_END;
+}
+
+void midiWriter::insertNoteEvent( InstrumentTrack *track )
+{
+ MidiEvent * event;
+ for( TrackContentObject * tco : track->getTCOs() )
+ {
+ tick_t offset = tco->startPosition().getTicks();
+ const NoteVector ¬es = dynamic_cast< Pattern * >( tco )->notes();
+
+ for( Note * i : notes )
+ {
+ flagCurrentTrackHasNotes = true;
+
+ event = new MidiEvent( MidiNoteOn );
+ // TODO: How can i set the channel?
+ event->setChannel( currentChannel );
+ event->setParam( 0, i->key()+12 );
+ event->setParam( 1, i->getVolume()*127/200 );
+ EventList.insert( i->pos().getTicks()+offset, event );
+
+ event = new MidiEvent( MidiNoteOff );
+ event->setChannel( currentChannel );
+ event->setParam( 0, i->key()+12 );
+ event->setParam( 1, 64 );
+ EventList.insert( i->endPos().getTicks()+offset, event );
+ }
+ }
+}
+
+void midiWriter::insertProgramEvent( InstrumentTrack *track )
+{
+ MidiEvent * event;
+ INSERT_PROG_EVENT( 0, 0, dynamic_cast( track->instrument()->childModel( "patch" ) )->value() );
+
+ SEARCH_AUTOTRACKS_START( track->instrument()->childModel( "patch" ) );
+ INSERT_PROG_EVENT( i.key(), offset, i.value() );
+ SEARCH_AUTOTRACKS_END;
+}
+
+void midiWriter::insertPitchEvent( InstrumentTrack *track )
+{
+ MidiEvent * event;
+ int pitchBendMultiply;
+ pitchBendMultiply = track->pitchRangeModel()->value();
+ INSERT_PITCH_EVENT( 0, 0, track->pitchModel()->value() );
+
+ SEARCH_AUTOTRACKS_START( track->pitchModel() )
+ PROGRESSION_CALC_START;
+ FIND_PITCH_MULTYPLY( (i-1).key() + ii + offset );
+ INSERT_PITCH_EVENT( (i-1).key() + ii, offset, a_tco->valueAt( MidiTime( (i-1).key()+ii+offset ) ) );
+ PROGRESSION_CALC_END;
+
+ FIND_PITCH_MULTYPLY( i.key() + offset );
+ INSERT_PITCH_EVENT( i.key(), offset, i.value() );
+ SEARCH_AUTOTRACKS_END
+}
+
+void midiWriter::allocateChannel( InstrumentTrack *track )
+{
+ // patman not supported.
+ if( track->instrumentName() == "Sf2 Player" )
+ {
+ Instrument * tr_inst = track->instrument();
+
+ // Find drumkit.
+ if( dynamic_cast< IntModel *>( tr_inst->childModel( "bank" ) )->value() >= 128 )
+ {
+ currentChannel = DrumChannel;
+ return;
+ }
+
+ int8_t prog = dynamic_cast< IntModel * >( tr_inst->childModel( "patch" ) )->value();
+
+ // Step 1: Find unused channel
+ for( currentChannel = 0; currentChannel < MidiChannelCount; currentChannel++ )
+ {
+ if ( currentChannel == DrumChannel )
+ continue;
+
+ if ( ChannelProg[ currentChannel ] == -1 )
+ {
+ ChannelProg[ currentChannel ] = prog;
+ return;
+ }
+ }
+
+ // Step 2: If no unused channel found, find channel has the same program.
+ for( currentChannel = 0; currentChannel < MidiChannelCount; currentChannel++ )
+ {
+ if ( currentChannel == DrumChannel )
+ continue;
+
+ if( ChannelProg[ currentChannel ] == prog )
+ return;
+ }
+
+ // Step 3: If no suitable channel, give it randomly.
+ do
+ {
+ currentChannel = qrand() % 16;
+ } while( currentChannel == DrumChannel );
+ }
+}
+
+// Just for 2, 4, 8, 16 etc.
+int midiWriter::intSqrt( int n )
+{
+ int i;
+ for( i = 0; n >> i < 1; i++ );
+ return i;
+}
+
+// Slot
+void midiWriter::writeTrackEvent( int track )
+{
+ DataFile dataFile( DataFile::SongProject );
+ flagRpnPitchBendRangeSent = false;
+ flagCurrentTrackHasNotes = false;
+ currentChannel = -1;
+
+ EventList.clear();
+ PitchBendMultiply.clear();
+
+ if( track == 0 )
+ {
+ // Export by LMMS
+ m_seq->writeMetaEvent( 0, copyright_notice, EXPORTED_BY );
+ m_seq->writeMidiEvent( 0, system_exclusive, 5, GM_SYSEX );
+ insertTempoEvent();
+ insertTimeSigEvent();
+ }
+ else
+ {
+ // Write track name.
+ Track* currentTrack = m_tl[ InstrumentTracks[ track-1 ] ];
+ m_seq->writeMetaEvent( 0, sequence_name, currentTrack->name() );
+
+ // TODO: Get tracks and automation pattern.
+ InstrumentTrack *instTrack = dynamic_cast< InstrumentTrack* >( currentTrack );
+ instTrack->saveState( dataFile, dataFile.content() );
+ allocateChannel( instTrack );
+
+ // When read EventList, it will be reverse.
+ insertNoteEvent( instTrack );
+ insertCCEvent( instTrack );
+ insertPitchEvent( instTrack );
+ insertProgramEvent( instTrack );
+ }
+
+ // Write event to file;
+ if( flagCurrentTrackHasNotes || track == 0 )
+ writeEventToFile();
+ // Do not forget ending this track.
+ m_seq->writeMetaEvent( 0, end_of_track );
+}
diff --git a/plugins/MidiExport/midiWriter.h b/plugins/MidiExport/midiWriter.h
new file mode 100644
index 00000000000..79d8bf34d02
--- /dev/null
+++ b/plugins/MidiExport/midiWriter.h
@@ -0,0 +1,99 @@
+/*
+ * midiWriter.h - export backend.
+ *
+ * Copyright (c) 2016-2017 Tony Chyi
+ *
+ * This file is part of LMMS - http://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 MIDI_WRITER_H
+#define MIDI_WRITER_H
+
+#include
+#include
+
+#include "AutomationPattern.h"
+#include "Engine.h"
+#include "MeterModel.h"
+#include "Midi.h"
+#include "MidiEvent.h"
+#include "TrackContainer.h"
+
+#define EXPORTED_BY QString( "Exported by LMMS" )
+#define GM_SYSEX QByteArray( "\x7e\x7f\x09\x01" ) + (char) end_of_sysex
+
+const int DefaultPitchMultiply = 2;
+// If TimSig = 4/4, set midi clock = 24;
+const int DefaultMidiClockPerMetronomeClick = 24;
+const int DefaultMidiDivision = 960;
+// Channel 10 in SMF is drum kit.
+const int DrumChannel = 9;
+
+class midiWriter: public QObject
+{
+ Q_OBJECT
+public:
+ midiWriter( const TrackContainer::TrackList &tracks );
+ ~midiWriter();
+ void writeFile( const QString &fileName );
+
+public slots:
+ void writeTrackEvent( int track );
+
+private:
+ /*
+ * Write event to file here. But need to delete event after write,
+ * otherwise it will cause memory leak.
+ */
+ void writeEventToFile();
+
+ // All methods below will create new MidiEvent, MUST delete it later!
+ void insertTempoEvent();
+ void insertTimeSigEvent();
+ void insertNoteEvent( InstrumentTrack *track );
+
+ void insertCCEvent( InstrumentTrack *track );
+ void insertProgramEvent( InstrumentTrack *track );
+ void insertPitchEvent( InstrumentTrack *track );
+
+ void allocateChannel(InstrumentTrack *track );
+ int intSqrt( int n );
+
+ const TrackContainer::TrackList &m_tl;
+ drumstick::QSmf *m_seq;
+
+ QMultiMap< long, MidiEvent* > EventList;
+ QMap< int, int > PitchBendMultiply;
+ QList< int > InstrumentTracks;
+ QList< int > AutomationTracks;
+ int8_t ChannelProg[ MidiChannelCount ];
+
+ AutomationPattern* tempoPat;
+ AutomationPattern* timeSigNumPat;
+ AutomationPattern* timeSigDenPat;
+
+ double tickRate;
+ int8_t currentChannel;
+
+ // LMMS just only export pitch bend range.
+ bool flagRpnPitchBendRangeSent;
+ bool flagCurrentTrackHasNotes;
+};
+
+#endif
diff --git a/plugins/MidiImport/CMakeLists.txt b/plugins/MidiImport/CMakeLists.txt
deleted file mode 100644
index 9fd6b6876e5..00000000000
--- a/plugins/MidiImport/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-INCLUDE(BuildPlugin)
-
-BUILD_PLUGIN(midiimport MidiImport.cpp MidiImport.h
- portsmf/allegro.cpp portsmf/allegro.h portsmf/allegrosmfwr.cpp
- portsmf/allegrord.cpp portsmf/allegrowr.cpp portsmf/allegrosmfrd.cpp
- portsmf/mfmidi.cpp portsmf/mfmidi.h portsmf/strparse.cpp
- portsmf/strparse.h portsmf/algrd_internal.h portsmf/algsmfrd_internal.h
- portsmf/trace.h MOCFILES MidiImport.h)
diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp
deleted file mode 100644
index e5a9b02a31c..00000000000
--- a/plugins/MidiImport/MidiImport.cpp
+++ /dev/null
@@ -1,618 +0,0 @@
-/*
- * MidiImport.cpp - support for importing MIDI files
- *
- * Copyright (c) 2005-2014 Tobias Doerffel
- *
- * This file is part of LMMS - http://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.
- *
- */
-
-
-#include
-#include
-#include
-#include
-#include
-
-#include "MidiImport.h"
-#include "TrackContainer.h"
-#include "InstrumentTrack.h"
-#include "AutomationTrack.h"
-#include "AutomationPattern.h"
-#include "ConfigManager.h"
-#include "Pattern.h"
-#include "Instrument.h"
-#include "GuiApplication.h"
-#include "MainWindow.h"
-#include "MidiTime.h"
-#include "debug.h"
-#include "embed.h"
-#include "Song.h"
-
-#include "portsmf/allegro.h"
-
-#define makeID(_c0, _c1, _c2, _c3) \
- ( 0 | \
- ( ( _c0 ) | ( ( _c1 ) << 8 ) | ( ( _c2 ) << 16 ) | ( ( _c3 ) << 24 ) ) )
-
-
-
-extern "C"
-{
-
-Plugin::Descriptor PLUGIN_EXPORT midiimport_plugin_descriptor =
-{
- STRINGIFY( PLUGIN_NAME ),
- "MIDI Import",
- QT_TRANSLATE_NOOP( "pluginBrowser",
- "Filter for importing MIDI-files into LMMS" ),
- "Tobias Doerffel ",
- 0x0100,
- Plugin::ImportFilter,
- NULL,
- NULL,
- NULL
-} ;
-
-}
-
-
-MidiImport::MidiImport( const QString & _file ) :
- ImportFilter( _file, &midiimport_plugin_descriptor ),
- m_events(),
- m_timingDivision( 0 )
-{
-}
-
-
-
-
-MidiImport::~MidiImport()
-{
-}
-
-
-
-
-bool MidiImport::tryImport( TrackContainer* tc )
-{
- if( openFile() == false )
- {
- return false;
- }
-
-#ifdef LMMS_HAVE_FLUIDSYNTH
- if( gui != NULL &&
- ConfigManager::inst()->defaultSoundfont().isEmpty() )
- {
- QMessageBox::information( gui->mainWindow(),
- tr( "Setup incomplete" ),
- tr( "You do not have set up a default soundfont in "
- "the settings dialog (Edit->Settings). "
- "Therefore no sound will be played back after "
- "importing this MIDI file. You should download "
- "a General MIDI soundfont, specify it in "
- "settings dialog and try again." ) );
- }
-#else
- if( gui )
- {
- QMessageBox::information( gui->mainWindow(),
- tr( "Setup incomplete" ),
- tr( "You did not compile LMMS with support for "
- "SoundFont2 player, which is used to add default "
- "sound to imported MIDI files. "
- "Therefore no sound will be played back after "
- "importing this MIDI file." ) );
- }
-#endif
-
- switch( readID() )
- {
- case makeID( 'M', 'T', 'h', 'd' ):
- printf( "MidiImport::tryImport(): found MThd\n");
- return readSMF( tc );
-
- case makeID( 'R', 'I', 'F', 'F' ):
- printf( "MidiImport::tryImport(): found RIFF\n");
- return readRIFF( tc );
-
- default:
- printf( "MidiImport::tryImport(): not a Standard MIDI "
- "file\n" );
- return false;
- }
-}
-
-
-
-
-class smfMidiCC
-{
-
-public:
- smfMidiCC() :
- at( NULL ),
- ap( NULL ),
- lastPos( 0 )
- { }
-
- AutomationTrack * at;
- AutomationPattern * ap;
- MidiTime lastPos;
-
- smfMidiCC & create( TrackContainer* tc, QString tn )
- {
- if( !at )
- {
- // Keep LMMS responsive, for now the import runs
- // in the main thread. This should probably be
- // removed if that ever changes.
- qApp->processEvents();
- at = dynamic_cast( Track::create( Track::AutomationTrack, tc ) );
- }
- if( tn != "") {
- at->setName( tn );
- }
- return *this;
- }
-
-
- void clear()
- {
- at = NULL;
- ap = NULL;
- lastPos = 0;
- }
-
-
- smfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value )
- {
- if( !ap || time > lastPos + DefaultTicksPerTact )
- {
- MidiTime pPos = MidiTime( time.getTact(), 0 );
- ap = dynamic_cast(
- at->createTCO(0) );
- ap->movePosition( pPos );
- ap->addObject( objModel );
- }
-
- lastPos = time;
- time = time - ap->startPosition();
- ap->putValue( time, value, false );
- ap->changeLength( MidiTime( time.getTact() + 1, 0 ) );
-
- return *this;
- }
-};
-
-
-
-class smfMidiChannel
-{
-
-public:
- smfMidiChannel() :
- it( NULL ),
- p( NULL ),
- it_inst( NULL ),
- isSF2( false ),
- hasNotes( false ),
- lastEnd( 0 )
- { }
-
- InstrumentTrack * it;
- Pattern* p;
- Instrument * it_inst;
- bool isSF2;
- bool hasNotes;
- MidiTime lastEnd;
- QString trackName;
-
- smfMidiChannel * create( TrackContainer* tc, QString tn )
- {
- if( !it ) {
- // Keep LMMS responsive
- qApp->processEvents();
- it = dynamic_cast( Track::create( Track::InstrumentTrack, tc ) );
-
-#ifdef LMMS_HAVE_FLUIDSYNTH
- it_inst = it->loadInstrument( "sf2player" );
-
- if( it_inst )
- {
- isSF2 = true;
- it_inst->loadFile( ConfigManager::inst()->defaultSoundfont() );
- it_inst->childModel( "bank" )->setValue( 0 );
- it_inst->childModel( "patch" )->setValue( 0 );
- }
- else
- {
- it_inst = it->loadInstrument( "patman" );
- }
-#else
- it_inst = it->loadInstrument( "patman" );
-#endif
- trackName = tn;
- if( trackName != "") {
- it->setName( tn );
- }
- lastEnd = 0;
- // General MIDI default
- it->pitchRangeModel()->setInitValue( 2 );
- }
- return this;
- }
-
-
- void addNote( Note & n )
- {
- if( !p || n.pos() > lastEnd + DefaultTicksPerTact )
- {
- MidiTime pPos = MidiTime( n.pos().getTact(), 0 );
- p = dynamic_cast( it->createTCO( 0 ) );
- p->movePosition( pPos );
- }
- hasNotes = true;
- lastEnd = n.pos() + n.length();
- n.setPos( n.pos( p->startPosition() ) );
- p->addNote( n, false );
- }
-
-};
-
-
-bool MidiImport::readSMF( TrackContainer* tc )
-{
- QString filename = file().fileName();
- closeFile();
-
- const int preTrackSteps = 2;
- QProgressDialog pd( TrackContainer::tr( "Importing MIDI-file..." ),
- TrackContainer::tr( "Cancel" ), 0, preTrackSteps, gui->mainWindow() );
- pd.setWindowTitle( TrackContainer::tr( "Please wait..." ) );
- pd.setWindowModality(Qt::WindowModal);
- pd.setMinimumDuration( 0 );
-
- pd.setValue( 0 );
-
- Alg_seq_ptr seq = new Alg_seq(filename.toLocal8Bit(), true);
- seq->convert_to_beats();
-
- pd.setMaximum( seq->tracks() + preTrackSteps );
- pd.setValue( 1 );
-
- // 128 CC + Pitch Bend
- smfMidiCC ccs[129];
- smfMidiChannel chs[256];
-
- MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel();
- AutomationPattern * timeSigNumeratorPat =
- AutomationPattern::globalAutomationPattern( &timeSigMM.numeratorModel() );
- AutomationPattern * timeSigDenominatorPat =
- AutomationPattern::globalAutomationPattern( &timeSigMM.denominatorModel() );
-
- // TODO: adjust these to Time.Sig changes
- double beatsPerTact = 4;
- double ticksPerBeat = DefaultTicksPerTact / beatsPerTact;
-
- // Time-sig changes
- Alg_time_sigs * timeSigs = &seq->time_sig;
- for( int s = 0; s < timeSigs->length(); ++s )
- {
- Alg_time_sig timeSig = (*timeSigs)[s];
- // Initial timeSig, set song-default value
- if(/* timeSig.beat == 0*/ true )
- {
- // TODO set song-global default value
- printf("Another timesig at %f\n", timeSig.beat);
- timeSigNumeratorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.num );
- timeSigDenominatorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.den );
- }
- else
- {
- }
-
- }
-
- pd.setValue( 2 );
-
- // Tempo stuff
- AutomationPattern * tap = tc->tempoAutomationPattern();
- if( tap )
- {
- tap->clear();
- Alg_time_map * timeMap = seq->get_time_map();
- Alg_beats & beats = timeMap->beats;
- for( int i = 0; i < beats.len - 1; i++ )
- {
- Alg_beat_ptr b = &(beats[i]);
- double tempo = ( beats[i + 1].beat - b->beat ) /
- ( beats[i + 1].time - beats[i].time );
- tap->putValue( b->beat * ticksPerBeat, tempo * 60.0 );
- }
- if( timeMap->last_tempo_flag )
- {
- Alg_beat_ptr b = &( beats[beats.len - 1] );
- tap->putValue( b->beat * ticksPerBeat, timeMap->last_tempo * 60.0 );
- }
- }
-
- // Song events
- for( int e = 0; e < seq->length(); ++e )
- {
- Alg_event_ptr evt = (*seq)[e];
-
- if( evt->is_update() )
- {
- printf("Unhandled SONG update: %d %f %s\n",
- evt->get_type_code(), evt->time, evt->get_attribute() );
- }
- }
-
- // Tracks
- for( int t = 0; t < seq->tracks(); ++t )
- {
- QString trackName = QString( tr( "Track" ) + " %1" ).arg( t );
- Alg_track_ptr trk = seq->track( t );
- pd.setValue( t + preTrackSteps );
-
- for( int c = 0; c < 129; c++ )
- {
- ccs[c].clear();
- }
-
- // Now look at events
- for( int e = 0; e < trk->length(); ++e )
- {
- Alg_event_ptr evt = (*trk)[e];
-
- if( evt->chan == -1 )
- {
- bool handled = false;
- if( evt->is_update() )
- {
- QString attr = evt->get_attribute();
- if( attr == "tracknames" && evt->get_update_type() == 's' ) {
- trackName = evt->get_string_value();
- handled = true;
- }
- }
- if( !handled ) {
- // Write debug output
- printf("MISSING GLOBAL HANDLER\n");
- printf(" Chn: %d, Type Code: %d, Time: %f", (int) evt->chan,
- evt->get_type_code(), evt->time );
- if ( evt->is_update() )
- {
- printf( ", Update Type: %s", evt->get_attribute() );
- if ( evt->get_update_type() == 'a' )
- {
- printf( ", Atom: %s", evt->get_atom_value() );
- }
- }
- printf( "\n" );
- }
- }
- else if( evt->is_note() && evt->chan < 256 )
- {
- smfMidiChannel * ch = chs[evt->chan].create( tc, trackName );
- Alg_note_ptr noteEvt = dynamic_cast( evt );
- int ticks = noteEvt->get_duration() * ticksPerBeat;
- Note n( (ticks < 1 ? 1 : ticks ),
- noteEvt->get_start_time() * ticksPerBeat,
- noteEvt->get_identifier() - 12,
- noteEvt->get_loud());
- ch->addNote( n );
-
- }
-
- else if( evt->is_update() )
- {
- smfMidiChannel * ch = chs[evt->chan].create( tc, trackName );
-
- double time = evt->time*ticksPerBeat;
- QString update( evt->get_attribute() );
-
- if( update == "programi" )
- {
- long prog = evt->get_integer_value();
- if( ch->isSF2 )
- {
- ch->it_inst->childModel( "bank" )->setValue( 0 );
- ch->it_inst->childModel( "patch" )->setValue( prog );
- }
- else {
- const QString num = QString::number( prog );
- const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat";
- const QString dir = "/usr/share/midi/"
- "freepats/Tone_000/";
- const QStringList files = QDir( dir ).
- entryList( QStringList( filter ) );
- if( ch->it_inst && !files.empty() )
- {
- ch->it_inst->loadFile( dir+files.front() );
- }
- }
- }
-
- else if( update.startsWith( "control" ) || update == "bendr" )
- {
- int ccid = update.mid( 7, update.length()-8 ).toInt();
- if( update == "bendr" )
- {
- ccid = 128;
- }
- if( ccid <= 128 )
- {
- double cc = evt->get_real_value();
- AutomatableModel * objModel = NULL;
-
- switch( ccid )
- {
- case 0:
- if( ch->isSF2 && ch->it_inst )
- {
- objModel = ch->it_inst->childModel( "bank" );
- printf("BANK SELECT %f %d\n", cc, (int)(cc*127.0));
- cc *= 127.0f;
- }
- break;
-
- case 7:
- objModel = ch->it->volumeModel();
- cc *= 100.0f;
- break;
-
- case 10:
- objModel = ch->it->panningModel();
- cc = cc * 200.f - 100.0f;
- break;
-
- case 128:
- objModel = ch->it->pitchModel();
- cc = cc * 100.0f;
- break;
- default:
- //TODO: something useful for other CCs
- break;
- }
-
- if( objModel )
- {
- if( time == 0 && objModel )
- {
- objModel->setInitValue( cc );
- }
- else
- {
- if( ccs[ccid].at == NULL ) {
- ccs[ccid].create( tc, trackName + " > " + (
- objModel != NULL ?
- objModel->displayName() :
- QString("CC %1").arg(ccid) ) );
- }
- ccs[ccid].putValue( time, objModel, cc );
- }
- }
- }
- }
- else {
- printf("Unhandled update: %d %d %f %s\n", (int) evt->chan,
- evt->get_type_code(), evt->time, evt->get_attribute() );
- }
- }
- }
- }
-
- delete seq;
-
-
- for( int c=0; c < 256; ++c )
- {
- if( !chs[c].hasNotes && chs[c].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 );
- }
-
- return true;
-}
-
-
-
-
-bool MidiImport::readRIFF( TrackContainer* tc )
-{
- // skip file length
- skip( 4 );
-
- // check file type ("RMID" = RIFF MIDI)
- if( readID() != makeID( 'R', 'M', 'I', 'D' ) )
- {
-invalid_format:
- qWarning( "MidiImport::readRIFF(): invalid file format" );
- return false;
- }
-
- // search for "data" chunk
- while( 1 )
- {
- const int id = readID();
- const int len = read32LE();
- if( file().atEnd() )
- {
-data_not_found:
- qWarning( "MidiImport::readRIFF(): data chunk not found" );
- return false;
- }
- if( id == makeID( 'd', 'a', 't', 'a' ) )
- {
- break;
- }
- if( len < 0 )
- {
- goto data_not_found;
- }
- skip( ( len + 1 ) & ~1 );
- }
-
- // the "data" chunk must contain data in SMF format
- if( readID() != makeID( 'M', 'T', 'h', 'd' ) )
- {
- goto invalid_format;
- }
- return readSMF( tc );
-}
-
-
-
-
-void MidiImport::error()
-{
- printf( "MidiImport::readTrack(): invalid MIDI data (offset %#x)\n",
- (unsigned int) file().pos() );
-}
-
-
-
-extern "C"
-{
-
-// necessary for getting instance out of shared lib
-Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
-{
- return new MidiImport( QString::fromUtf8(
- static_cast( _data ) ) );
-}
-
-
-}
-
diff --git a/plugins/MidiImport/portsmf/README.txt b/plugins/MidiImport/portsmf/README.txt
deleted file mode 100644
index 0f36c9e4ee7..00000000000
--- a/plugins/MidiImport/portsmf/README.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-portsmf README.txt
-14 Jun 2008
-Roger B. Dannenberg
-
-Portsmf is "Port Standard MIDI File", a cross-platform, C++ library
-for reading and writing Standard MIDI Files.
-
-License information: free and open source, see license.txt for details
-
-Features:
-
-- input and output of Standard MIDI Files
-- data structures, classes, etc. for representing music data in memory
- o sequence structure consisting of multiple tracks
- o track structure consisting of multiple events
- o events contain note and control data
- o extensible attribute-value property lists
- o tempo track and time signature representation
-- input and output of a text-based representation: Allegro files
-- extensive editing operations on sequences and tracks
-- conversion to/from binary buffers for archiving, undo/redo, etc.
-
-Portsmf is a relatively small number of about 9 files, so there is
-currently no support for building/maintaining Portsmf as a separate
-library. (Contributions are welcome.) For now, it is suggested that
-you simply compile these files along with your application sources.
-
-There is a test program in portsmf_test and makefiles to build it as
-an example.
-
-You might want to browse through portsmf_test/allegro_test.cpp
-for examples that use and exercise most of the portsmf functions.
diff --git a/plugins/MidiImport/portsmf/algrd_internal.h b/plugins/MidiImport/portsmf/algrd_internal.h
deleted file mode 100644
index 3b77adc4cd2..00000000000
--- a/plugins/MidiImport/portsmf/algrd_internal.h
+++ /dev/null
@@ -1,4 +0,0 @@
-/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */
-
-Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq);
-
diff --git a/plugins/MidiImport/portsmf/algsmfrd_internal.h b/plugins/MidiImport/portsmf/algsmfrd_internal.h
deleted file mode 100644
index 75cc0093bc7..00000000000
--- a/plugins/MidiImport/portsmf/algsmfrd_internal.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/* algsmfrd_internal.h -- interface from allegrosmfrd.cpp to allegro.cpp */
-
-Alg_error alg_smf_read(std::istream &file, Alg_seq_ptr new_seq);
diff --git a/plugins/MidiImport/portsmf/allegro.cpp b/plugins/MidiImport/portsmf/allegro.cpp
deleted file mode 100644
index 653ff4c07db..00000000000
--- a/plugins/MidiImport/portsmf/allegro.cpp
+++ /dev/null
@@ -1,2866 +0,0 @@
-// Allegro: music representation system, with
-// extensible in-memory sequence structure
-// upward compatible with MIDI
-// implementations in C++ and Serpent
-// external, text-based representation
-// compatible with Aura
-//
-/* CHANGE LOG:
-04 apr 03 -- fixed bug in add_track that caused infinite loop
-*/
-
-#include "debug.h"
-#include "stdlib.h"
-#include "stdio.h"
-#include "string.h"
-#include "memory.h"
-#include
-#include
-using namespace std;
-#include "allegro.h"
-#include "algrd_internal.h"
-#include "algsmfrd_internal.h"
-// #include "trace.h" -- only needed for debugging
-#include "math.h"
-
-#define STREQL(x, y) (strcmp(x, y) == 0)
-#define MAX(x, y) ((x) > (y) ? (x) : (y))
-
-// 4311 is type cast ponter to long warning
-// 4996 is warning against strcpy
-// 4267 is size_t to long warning
-//#pragma warning(disable: 4311 4996 4267)
-Alg_atoms symbol_table;
-Serial_buffer Alg_track::ser_buf; // declare the static variable
-
-bool within(double d1, double d2, double epsilon)
-{
- d1 -= d2;
- return d1 < epsilon && d1 > -epsilon;
-}
-
-
-char *heapify(const char *s)
-{
- char *h = new char[strlen(s) + 1];
- strcpy(h, s);
- return h;
-}
-
-
-void Alg_atoms::expand()
-{
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- char **new_atoms = new Alg_attribute[maxlen];
- // now do copy
- memcpy(new_atoms, atoms, len * sizeof(Alg_attribute));
- if (atoms) delete[] atoms;
- atoms = new_atoms;
-}
-
-
-// insert_new -- insert an attribute name and type
-//
-// attributes are stored as a string consisting of the type
-// (a char) followed by the attribute name. This makes it
-// easy to retrieve the type or the name or both.
-//
-Alg_attribute Alg_atoms::insert_new(const char *name, char attr_type)
-{
- if (len == maxlen) expand();
- char *h = new char[strlen(name) + 2];
- strcpy(h + 1, name);
- *h = attr_type;
- atoms[len++] = h;
- return h;
-}
-
-
-Alg_attribute Alg_atoms::insert_attribute(Alg_attribute attr)
-{
- for (int i = 0; i < len; i++) {
- if (STREQL(attr, atoms[i])) {
- return atoms[i];
- }
- }
- return insert_new(attr + 1, attr[0]);
-}
-
-
-Alg_attribute Alg_atoms::insert_string(const char *name)
-{
- char attr_type = name[strlen(name) - 1];
- for (int i = 0; i < len; i++) {
- if (attr_type == atoms[i][0] &&
- STREQL(name, atoms[i] + 1)) {
- return atoms[i];
- }
- }
- return insert_new(name, attr_type);
-}
-
-
-void Alg_parameter::copy(Alg_parameter_ptr parm)
-{
- *this = *parm; // copy all fields
- // if the value is a string, copy the string
- if (attr_type() == 's') {
- s = heapify(s);
- }
-}
-
-
-void Alg_parameter::show()
-{
- switch (attr[0]) {
- case 'r':
- printf("%s:%g", attr_name(), r);
- break;
- case 's':
- printf("%s:%s", attr_name(), s);
- break;
- case 'i':
- printf("%s:%d", attr_name(), (int) i);
- break;
- case 'l':
- printf("%s:%s", attr_name(), (l ? "t" : "f"));
- break;
- case 'a':
- printf("%s:%s", attr_name(), a);
- break;
- }
-}
-
-
-Alg_parameter::~Alg_parameter()
-{
- if (attr_type() == 's' && s) {
- delete[] s;
- }
-}
-
-
-void Alg_parameters::insert_real(Alg_parameters **list, char *name, double r)
-{
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.r = r;
- assert(a->parm.attr_type() == 'r');
-}
-
-
-void Alg_parameters::insert_string(Alg_parameters **list, char *name, char *s)
-{
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- // string is deleted when parameter is deleted
- a->parm.s = heapify(s);
- assert(a->parm.attr_type() == 's');
-}
-
-
-void Alg_parameters::insert_integer(Alg_parameters **list, char *name, long i)
-{
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.i = i;
- assert(a->parm.attr_type() == 'i');
-}
-
-
-void Alg_parameters::insert_logical(Alg_parameters **list, char *name, bool l)
-{
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.l = l;
- assert(a->parm.attr_type() == 'l');
-}
-
-
-void Alg_parameters::insert_atom(Alg_parameters **list, char *name, char *s)
-{
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.a = symbol_table.insert_string(s);
- assert(a->parm.attr_type() == 'a');
-}
-
-
-Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, const char *name)
-{
- while (*list) {
- if (STREQL((*list)->parm.attr_name(), name)) {
- Alg_parameters_ptr p = *list;
- *list = p->next;
- p->next = NULL;
- return p; // caller should free this pointer
- }
- list = &((*list)->next);
- }
- return NULL;
-}
-
-
-Alg_parameter_ptr Alg_parameters::find(Alg_attribute *attr)
-{
- assert(attr);
- Alg_parameters_ptr temp = this;
- while (temp) {
- if (temp->parm.attr == *attr) {
- return &(temp->parm);
- }
- }
- return NULL;
-}
-
-
-int Alg_event::get_type_code()
-{
- if (!is_note()) {
- const char* attr = get_attribute();
- if (STREQL(attr, "gate")) // volume change
- return ALG_GATE;
- if (STREQL(attr, "bend")) // pitch bend
- return ALG_BEND;
- if (strncmp(attr, "control", 7) == 0) // control change
- // note that midi control changes have attributes of the form
- // "control" where n is the decimal number (as a character string)
- // of the midi controller, e.g. control2 is the breath controller.
- // 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
- return ALG_PROGRAM;
- if (STREQL(attr, "pressure")) // pressure change
- return ALG_PRESSURE;
- if (STREQL(attr, "keysig")) // key signature
- return ALG_KEYSIG;
- if (STREQL(attr, "timesig_num")) // time signature numerator
- return ALG_TIMESIG_NUM;
- if (STREQL(attr, "timesig_den")) // time signature denominator
- return ALG_TIMESIG_DEN;
- return ALG_OTHER;
- }
- return ALG_NOTE; // it is a note
-}
-
-
-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));
- if (!parm) {
- note->parameters = new Alg_parameters(note->parameters);
- parm = &(note->parameters->parm);
- }
- } else { // update
- Alg_update_ptr update = (Alg_update_ptr) this;
- parm = &(update->parameter);
- }
- parm->copy(new_parameter); // copy entire parameter
-}
-
-
-void Alg_event::set_string_value(char *a, char *value)
-{
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 's');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.s = value;
- set_parameter(&parm);
- parm.s = NULL; // do this to prevent string from being freed
-}
-
-
-void Alg_event::set_real_value(char *a, double value)
-{
- assert(a); // must be non-null
- // attr is like a, but it has the type code prefixed for
- // fast lookup, and it is a unique string in symbol_table
- // e.g. a="attackr" -> attr="rattackr"
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'r');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.r = value;
- set_parameter(&parm);
- // since type is 'r' we don't have to NULL the string
-}
-
-
-void Alg_event::set_logical_value(char *a, bool value)
-{
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'l');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.l = value;
- set_parameter(&parm);
- // since type is 'l' we don't have to NULL the string
-}
-
-
-void Alg_event::set_integer_value(char *a, long value)
-{
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'i');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.i = value;
- set_parameter(&parm);
- // since tpye is 'i' we don't have to NULL the string
-}
-
-
-void Alg_event::set_atom_value(char *a, char *value)
-{
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'a');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.a = value;
- set_parameter(&parm);
- /* since type is 'a' we don't have to null the string */
-}
-
-
-float Alg_event::get_pitch()
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->pitch;
-}
-
-
-float Alg_event::get_loud()
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->loud;
-}
-
-
-double Alg_event::get_start_time()
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->time;
-}
-
-
-double Alg_event::get_end_time()
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->time + note->dur;
-}
-
-
-double Alg_event::get_duration()
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->dur;
-}
-
-
-void Alg_event::set_pitch(float p)
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- note->pitch = p;
-}
-
-void Alg_event::set_loud(float l)
-{
- assert(is_note());
- Alg_note *note = (Alg_note *) this;
- note->loud = l;
-}
-
-
-void Alg_event::set_duration(double d)
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- note->dur = d;
-}
-
-
-bool Alg_event::has_attribute(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);
- return parm != NULL;
-}
-
-
-char Alg_event::get_attribute_type(char *a)
-{
- assert(is_note());
- assert(a);
- return a[strlen(a) - 1];
-}
-
-
-char *Alg_event::get_string_value(char *a, 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);
- if (parm) return parm->s;
- return value;
-}
-
-
-double Alg_event::get_real_value(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);
- if (parm) return parm->r;
- return value;
-}
-
-
-bool Alg_event::get_logical_value(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);
- if (parm) return parm->l;
- return value;
-}
-
-
-long Alg_event::get_integer_value(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);
- if (parm) return parm->i;
- return value;
-}
-
-
-char *Alg_event::get_atom_value(char *a, 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);
- if (parm) return parm->a;
- // if default is a string, convert to an atom (unique
- // string in symbol table) and return it
- return (value == NULL ? NULL :
- symbol_table.insert_string(value));
-}
-
-
-void Alg_event::delete_attribute(char *a)
-{
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- Alg_parameters::remove_key(&(note->parameters), a);
-}
-
-
-const char *Alg_event::get_attribute()
-// Note: this returns a string, not an Alg_attribute
-{
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- return update->parameter.attr_name();
-}
-
-
-char Alg_event::get_update_type()
-{
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- return update->parameter.attr_type();
-}
-
-
-char *Alg_event::get_string_value()
-{
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 's');
- return update->parameter.a;
-}
-
-
-double Alg_event::get_real_value()
-{
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'r');
- return update->parameter.r;
-}
-
-
-bool Alg_event::get_logical_value()
-{
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'l');
- return update->parameter.l;
-}
-
-
-long Alg_event::get_integer_value()
-{
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'i');
- return update->parameter.i;
-}
-
-
-char *Alg_event::get_atom_value()
-{
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'a');
- return update->parameter.a;
-}
-
-
-bool Alg_event::overlap(double t, double len, bool all)
-{
- // event starts within region
- if (time >= t && time <= t + len - ALG_EPS)
- return true;
- if (all && is_note()) {
- double dur = ((Alg_note_ptr) this)->dur;
- // note ends within region
- if (time < t && time + dur - ALG_EPS > t)
- return true;
- }
- // does not overlap
- return false;
-}
-
-
-Alg_note::Alg_note(Alg_note_ptr note)
-{
- *this = *note; // copy all fields
- // parameters is now a shared pointer. We need to copy the
- // parameters
- Alg_parameters_ptr next_param_ptr = parameters;
- while (next_param_ptr) {
- Alg_parameters_ptr new_params = new Alg_parameters(next_param_ptr->next);
- new_params->parm.copy(&(next_param_ptr->parm)); // copy the attribute and value
- next_param_ptr = new_params->next;
- }
-}
-
-
-Alg_note::~Alg_note()
-{
- while (parameters) {
- Alg_parameters_ptr to_delete = parameters;
- parameters = parameters->next;
- delete to_delete;
- }
-}
-
-
-void Alg_note::show()
-{
- printf("Alg_note: time %g, chan %d, dur %g, key %d, "
- "pitch %g, loud %g, attributes ",
- time, (int) chan, dur, (int) key, pitch, loud);
- Alg_parameters_ptr parms = parameters;
- while (parms) {
- parms->parm.show();
- printf(" ");
- parms = parms->next;
- }
- printf("\n");
-}
-
-
-Alg_update::Alg_update(Alg_update_ptr update)
-{
- *this = *update; // copy all fields
- // parameter requires careful copy to possibly duplicate string value:
- this->parameter.copy(&(update->parameter));
-}
-
-
-void Alg_update::show()
-{
- printf("Alg_update: ");
- parameter.show();
- printf("\n");
-}
-
-
-void Alg_events::expand()
-{
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- Alg_event_ptr *new_events = new Alg_event_ptr[maxlen];
- // now do copy
- memcpy(new_events, events, len * sizeof(Alg_event_ptr));
- if (events) delete[] events;
- events = new_events;
-}
-
-
-void Alg_events::insert(Alg_event_ptr event)
-{
- if (maxlen <= len) {
- expand();
- }
- // Note: if the new event is the last one, the assignment
- // events[i] = event; (below) will never execute, so just
- // in case, we do the assignment here. events[len] will
- // be replaced during the memmove() operation below if
- // this is not the last event.
- events[len] = event;
- len++;
- // find insertion point: (this could be a binary search)
- for (int i = 0; i < len; i++) {
- if (events[i]->time > event->time) {
- // insert event at i
- memmove(&events[i + 1], &events[i],
- sizeof(Alg_event_ptr) * (len - i - 1));
- events[i] = event;
- return;
- }
- }
-}
-
-Alg_event_ptr Alg_events::uninsert(long index)
-{
- assert(0 <= index && index < len);
- Alg_event_ptr event = events[index];
- memmove(events + index, events + index + 1,
- sizeof(Alg_event_ptr) * (len - index - 1));
- len--;
- return event;
-}
-
-
-void Alg_events::append(Alg_event_ptr event)
-{
- if (maxlen <= len) {
- expand();
- }
- events[len++] = event;
- // keep track of last note_off time
- if (event->is_note()) {
- Alg_note_ptr note = (Alg_note_ptr) event;
- double note_off = note->time + note->dur;
- if (note_off > last_note_off)
- last_note_off = note_off;
- }
-}
-
-
-Alg_events::~Alg_events()
-{
- // individual events are not deleted, only the array
- if (events) {
- delete[] events;
- }
-}
-
-
-Alg_event_list::Alg_event_list(Alg_track *owner)
-{
- events_owner = owner;
- sequence_number = owner->sequence_number;
- beat_dur = 0.0; real_dur = 0.0; type = 'e';
-}
-
-
-Alg_event_ptr &Alg_event_list::operator [](int i)
-{
- assert(i >= 0 && i < len);
- return events[i];
-}
-
-
-Alg_event_list::~Alg_event_list()
-{
- // note that the events contained in the list are not destroyed
-}
-
-
-void Alg_event_list::set_start_time(Alg_event *event, double t)
-{
- // For Alg_event_list, find the owner and do the update there
- // 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();
- if (type == 'e') { // this is an Alg_event_list
- // make sure the owner has not changed its event set
- assert(events_owner &&
- sequence_number == events_owner->sequence_number);
- // do the update on the owner
- events_owner->set_start_time(event, t);
- return;
- } else if (type == 't') { // this is an Alg_track
- // find the event in the track
- track_ptr = (Alg_track_ptr) this;
- // this should be a binary search since events are in time order
- // probably there should be member function to do the search
- for (index = 0; index < length(); index++) {
- if ((*track_ptr)[index] == event) goto found_event;
- }
- } else { // type == 's', an Alg_seq
- Alg_seq_ptr seq = (Alg_seq_ptr) this;
- for (i = 0; i < seq->tracks(); i++) {
- track_ptr = seq->track(i);
- // if you implemented binary search, you could call it
- // instead of this loop too.
- for (index = 0; index < track_ptr->length(); index++) {
- if ((*track_ptr)[index] == event) goto found_event;
- }
- }
- }
- assert(false); // event not found seq or track!
- found_event:
- // at this point, track[index] == event
- // we could be clever and figure out exactly what notes to move
- // but it is simpler to just remove the event and reinsert it:
- track_ptr->uninsert(index);
- event->time = t;
- track_ptr->insert(event);
-}
-
-
-void Alg_beats::expand()
-{
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- Alg_beat_ptr new_beats = new Alg_beat[maxlen];
- // now do copy
- memcpy(new_beats, beats, len * sizeof(Alg_beat));
- if (beats) delete[] beats;
- beats = new_beats;
-}
-
-
-void Alg_beats::insert(long i, Alg_beat_ptr beat)
-{
- assert(i >= 0 && i <= len);
- if (maxlen <= len) {
- expand();
- }
- memmove(&beats[i + 1], &beats[i], sizeof(Alg_beat) * (len - i));
- memcpy(&beats[i], beat, sizeof(Alg_beat));
- len++;
-}
-
-
-Alg_time_map::Alg_time_map(Alg_time_map *map)
-{
- refcount = 0;
- assert(map->beats[0].beat == 0 && map->beats[0].time == 0);
- assert(map->beats.len > 0);
- // new_beats[0] = map->beats[0];
- // this is commented because
- // both new_beats[0] and map->beats[0] should be (0, 0)
- for (int i = 1; i < map->beats.len; i++) {
- beats.insert(i, &map->beats[i]);
- }
- last_tempo = map->last_tempo;
- last_tempo_flag = map->last_tempo_flag;
-}
-
-
-void Alg_time_map::show()
-{
- printf("Alg_time_map: ");
- for (int i = 0; i < beats.len; i++) {
- Alg_beat &b = beats[i];
- printf("(%g, %g) ", b.time, b.beat);
- }
- printf("last tempo: %g\n", last_tempo);
-}
-
-
-long Alg_time_map::locate_time(double time)
-{
- int i = 0;
- while ((i < beats.len) && (time > beats[i].time)) {
- i++;
- }
- return i;
-}
-
-
-long Alg_time_map::locate_beat(double beat)
-{
- int i = 0;
- while ((i < beats.len) && (beat > beats[i].beat)) {
- i++;
- }
- return i;
-}
-
-
-double Alg_time_map::beat_to_time(double beat)
-{
- Alg_beat_ptr mbi;
- Alg_beat_ptr mbi1;
- if (beat <= 0) {
- return beat;
- }
- int i = locate_beat(beat);
- if (i == beats.len) {
- if (last_tempo_flag) {
- return beats[i - 1].time +
- (beat - beats[i - 1].beat) / last_tempo;
- } else if (i == 1) {
- return beat * 60.0 / ALG_DEFAULT_BPM;
- // so we use that as default allegro tempo too
- } else {
- mbi = &beats[i - 2];
- mbi1 = &beats[i - 1];
- }
- } else {
- mbi = &beats[i - 1];
- mbi1 = &beats[i];
- }
- // whether w 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;
-}
-
-
-double Alg_time_map::time_to_beat(double time)
-{
- Alg_beat_ptr mbi;
- Alg_beat_ptr mbi1;
- if (time <= 0.0) return time;
- int i = locate_time(time);
- if (i == beats.len) {
- if (last_tempo_flag) {
- return beats[i - 1].beat +
- (time - beats[i - 1].time) * last_tempo;
- } else if (i == 1) {
- return time * (ALG_DEFAULT_BPM / 60.0);
- } else {
- mbi = &beats[i - 2];
- mbi1 = &beats[i - 1];
- }
- } else {
- mbi = &beats[i - 1];
- mbi1 = & beats[i];
- }
- double time_dif = mbi1->time - mbi->time;
- double beat_dif = mbi1->beat - mbi->beat;
- return mbi->beat + (time - mbi->time) * beat_dif / time_dif;
-}
-
-
-void Alg_time_map::insert_beat(double time, double beat)
-{
- int i = locate_time(time); // i is insertion point
- if (i < beats.len && within(beats[i].time, time, 0.000001)) {
- // replace beat if time is already in the map
- beats[i].beat = beat;
- } else {
- Alg_beat point;
- point.beat = beat;
- point.time = time;
- beats.insert(i, &point);
- }
- // beats[i] contains new beat
- // make sure we didn't generate a zero tempo.
- // if so, space beats by one microbeat as necessary
- long j = i;
- if (j == 0) j = 1; // do not adjust beats[0]
- while (j < beats.len &&
- beats[j - 1].beat + 0.000001 >= beats[j].beat) {
- beats[j].beat = beats[j - 1].beat + 0.000001;
- j++;
- }
-}
-
-
-bool Alg_time_map::insert_tempo(double tempo, double beat)
-{
- tempo = tempo / 60.0; // convert to beats per second
- // change the tempo at the given beat until the next beat event
- if (beat < 0) return false;
- double time = beat_to_time(beat);
- long i = locate_time(time);
- if (i >= beats.len || !within(beats[i].time, time, 0.000001)) {
- insert_beat(time, beat);
- }
- // now i is index of beat where tempo will change
- if (i == beats.len - 1) {
- last_tempo = tempo;
- // printf("last_tempo to %g\n", last_tempo);
- last_tempo_flag = true;
- } else { // adjust all future beats
- // compute the difference in beats
- double diff = beats[i + 1].beat - beats[i].beat;
- // convert beat difference to seconds at new tempo
- diff = diff / tempo;
- // figure out old time difference:
- double old_diff = beats[i + 1].time - time;
- // compute difference too
- diff = diff - old_diff;
- // apply new_diff to score and beats
- while (i < beats.len) {
- beats[i].time = beats[i].time + diff;
- i++;
- }
- }
- return true;
-}
-
-
-bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat)
-{
- if (start_beat >= end_beat) return false;
- // algorithm: insert a beat event if necessary at start_beat
- // and at end_beat
- // delete intervening map elements
- // change the tempo
- insert_beat(beat_to_time(start_beat), start_beat);
- insert_beat(beat_to_time(end_beat), end_beat);
- long start_x = locate_beat(start_beat) + 1;
- long stop_x = locate_beat(end_beat);
- while (stop_x < beats.len) {
- beats[start_x] = beats[stop_x];
- start_x++;
- stop_x++;
- }
- beats.len = start_x; // truncate the map to new length
- return insert_tempo(tempo, start_beat);
-}
-
-
-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
- // start and end are time in seconds if units_are_seconds is true
- int i = 0; // index into beats
- int start_index; // index of first breakpoint after start
- int count = 1;
- double initial_beat = start;
- double final_beat = end;
- if (units_are_seconds) {
- initial_beat = time_to_beat(start);
- final_beat = time_to_beat(end);
- } else {
- start = beat_to_time(initial_beat);
- end = beat_to_time(final_beat);
- }
- while (i < length() && beats[i].time < start) i++;
- // now i is index into beats of the first breakpoint after start
- // beats[0] is (0,0) and remains that way
- // copy beats[start_index] to beats[1], etc.
- // skip any beats at or near (start,initial_beat), using count
- // to keep track of how many entries there are
- start_index = i;
- while (i < length() && beats[i].time < end) {
- if (beats[i].time - start > ALG_EPS &&
- beats[i].beat - initial_beat > ALG_EPS) {
- beats[i].time = beats[i].time - start;
- beats[i].beat = beats[i].beat - initial_beat;
- beats[i - start_index + 1] = beats[i];
- count = count + 1;
- } else {
- start_index = start_index + 1;
- }
- i = i + 1;
- }
- // set last tempo data
- // we last examined beats[i-1] and copied it to
- // beats[i - start_index]. Next tempo should come
- // from beats[i] and store in beats[i - start_index + 1]
- // case 1: there is at least one breakpoint beyond end
- // => interpolate to put a breakpoint at end
- // case 2: no more breakpoints => set last tempo data
- if (i < length()) {
- // we know beats[i].time >= end, so case 1 applies
- beats[i - start_index + 1].time = end - start;
- beats[i - start_index + 1].beat = final_beat - initial_beat;
- count = count + 1;
- }
- // else we'll just use stored last tempo (if any)
- beats.len = count;
-}
-
-
-void Alg_time_map::cut(double start, double len, bool units_are_seconds)
-{
- // remove portion of time map from start to start + len,
- // shifting the tail left by len. start and len are in whatever
- // units the score is in. If you cut the time_map as well as cut
- // the tracks of the sequence, then sequences will preserve the
- // association between tempo changes and events
- double end = start + len;
- double initial_beat = start;
- double final_beat = end;
- int i = 0;
-
- if (units_are_seconds) {
- initial_beat = time_to_beat(start);
- final_beat = time_to_beat(end);
- } else {
- start = beat_to_time(initial_beat);
- end = beat_to_time(final_beat);
- len = end - start;
- }
- double beat_len = final_beat - initial_beat;
-
- while (i < length() && beats[i].time < start - ALG_EPS) {
- i = i + 1;
- }
-
- // if no beats exist at or after start, just return; nothing to cut
- if (i == length()) return;
-
- // now i is index into beats of the first breakpoint on or
- // after start, insert (start, initial_beat) in map
- if (i < length() && within(beats[i].time, start, ALG_EPS)) {
- // perterb time map slightly (within alg_eps) to place
- // break point exactly at the start time
- beats[i].time = start;
- beats[i].beat = initial_beat;
- } else {
- Alg_beat point(start, initial_beat);
- beats.insert(i, &point);
- }
- // now, we're correct up to beats[i] and beats[i] happens at start.
- // find first beat after end so we can start shifting from there
- i = i + 1;
- int start_index = i;
- while (i < length() && beats[i].time < end + ALG_EPS) i++;
- // now beats[i] is the next point to be included in beats
- // but from i onward, we must shift by (-len, -beat_len)
- while (i < length()) {
- Alg_beat &b = beats[i];
- b.time = b.time - len;
- b.beat = b.beat - beat_len;
- beats[start_index] = b;
- i = i + 1;
- start_index = start_index + 1;
- }
- beats.len = start_index;
-}
-
-
-void Alg_time_map::paste(double beat, Alg_track *tr)
-{
- // insert a given time map at a given time and dur (in beats)
- Alg_time_map_ptr from_map = tr->get_time_map();
- // printf("time map paste\nfrom map\n");
- // from_map->show();
- // printf("to map\n");
- // show();
- Alg_beats &from = from_map->beats;
- double time = beat_to_time(beat);
- // Locate the point at which dur occurs
- double dur = tr->get_beat_dur();
- double tr_end_time = from_map->beat_to_time(dur);
- // add offset to make room for insert
- int i = locate_beat(beat);
- while (i < length()) {
- beats[i].beat += dur;
- beats[i].time += tr_end_time;
- i++;
- }
- // printf("after opening up\n");
- // show();
- // insert point at beginning and end of paste
- insert_beat(time, beat);
- // printf("after beginning point insert\n");
- // show();
- // insert_beat(time + tr_end_time, beat + dur);
- // printf("after ending point insert\n");
- // show();
- int j = from_map->locate_beat(dur);
- for (i = 0; i < j; i++) {
- insert_beat(from[i].time + time, // shift by time
- from[i].beat + beat); // and beat
- }
- // printf("after inserts\n");
- show();
-}
-
-
-void Alg_time_map::insert_time(double start, double len)
-{
- // find time,beat pair that determines tempo at start
- // compute beat offset = (delta beat / delta time) * len
- // add len,beat offset to each following Alg_beat
- // show();
- int i = locate_time(start); // start <= beats[i].time
- if (beats[i].time == start) i++; // start < beats[i].time
- // case 1: between beats
- if (i > 0 && i < length()) {
- double beat_offset = len * (beats[i].beat - beats[i-1].beat) /
- (beats[i].time - beats[i-1].time);
- while (i < length()) {
- beats[i].beat += beat_offset;
- beats[i].time += len;
- i++;
- }
- } // otherwise, last tempo is in effect; nothing to do
- // printf("time_map AFTER INSERT\n");
- // show();
-}
-
-
-void Alg_time_map::insert_beats(double start, double len)
-{
- int i = locate_beat(start); // start <= beats[i].beat
- if (beats[i].beat == start) i++;
- if (i > 0 && i < length()) {
- double time_offset = len * (beats[i].time - beats[i-1].time) /
- (beats[i].beat - beats[i-1].beat);
- while (i < length()) {
- beats[i].time += time_offset;
- beats[i].beat += len;
- i++;
- }
- } // otherwise, last tempo is in effect; nothing to do
- // printf("time_map AFTER INSERT\n");
- // show();
-}
-
-
-Alg_track::Alg_track(Alg_time_map *map, bool seconds)
-{
- type = 't';
- time_map = NULL;
- units_are_seconds = seconds;
- set_time_map(map);
-}
-
-
-Alg_event_ptr Alg_track::copy_event(Alg_event_ptr event)
-{
- Alg_event *new_event;
- if (event->is_note()) {
- new_event = new Alg_note((Alg_note_ptr) event);
- } else { // update
- new_event = new Alg_update((Alg_update_ptr) event);
- }
- return new_event;
-}
-
-
-Alg_track::Alg_track(Alg_track &track)
-{
- type = 't';
- time_map = NULL;
- for (int i = 0; i < track.length(); i++) {
- append(copy_event(track.events[i]));
- }
- set_time_map(track.time_map);
- units_are_seconds = track.units_are_seconds;
-}
-
-
-Alg_track::Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map,
- bool units_are_seconds)
-{
- type = 't';
- time_map = NULL;
- for (int i = 0; i < event_list.length(); i++) {
- append(copy_event(event_list[i]));
- }
- set_time_map(map);
- this->units_are_seconds = units_are_seconds;
-}
-
-
-void Alg_track::serialize(void **buffer, long *bytes)
-{
- // first determine whether this is a seq or a track.
- // if it is a seq, then we will write the time map and a set of tracks
- // if it is a track, we just write the track data and not the time map
- //
- // The code will align doubles on ALIGN boundaries, and longs and
- // floats are aligned to multiples of 4 bytes.
- //
- // The format for a seq is:
- // 'ALGS' -- indicates that this is a sequence
- // long length of all seq data in bytes starting with 'ALGS'
- // long channel_offset_per_track
- // long units_are_seconds
- // time_map:
- // double last_tempo
- // long last_tempo_flag
- // long len -- number of tempo changes
- // for each tempo change (Alg_beat):
- // double time
- // double beat
- // time_sigs:
- // long len -- number of time_sigs
- // long pad
- // for each time signature:
- // double beat
- // double num
- // double den
- // tracks:
- // long len -- number of tracks
- // long pad
- // for each track:
- // 'ALGT' -- indicates this is a track
- // long length of all track data in bytes starting with 'ALGT'
- // long units_are_seconds
- // double beat_dur
- // double real_dur
- // long len -- number of events
- // for each event:
- // long selected
- // long type
- // long key
- // long channel
- // double time
- // if this is a note:
- // double pitch
- // double dur
- // double loud
- // long len -- number of parameters
- // for each parameter:
- // char attribute[] with zero pad to ALIGN
- // one of the following, depending on type:
- // double r
- // char s[] terminated by zero
- // long i
- // long l
- // char a[] terminated by zero
- // zero pad to ALIGN
- // else if this is an update
- // (same representation as parameter above)
- // zero pad to ALIGN
- //
- // The format for a track is given within the Seq format above
- assert(get_type() == 't');
- ser_buf.init_for_write();
- serialize_track();
- *buffer = ser_buf.to_heap(bytes);
-}
-
-
-void Alg_seq::serialize(void **buffer, long *bytes)
-{
- assert(get_type() == 's');
- ser_buf.init_for_write();
- serialize_seq();
- *buffer = ser_buf.to_heap(bytes);
-}
-
-
-void Serial_buffer::check_buffer(long needed)
-{
- if (len < (ptr - buffer) + needed) { // do we need more space?
- long new_len = len * 2; // exponential growth is important
- // initially, length is zero, so bump new_len to a starting value
- if (new_len == 0) new_len = 1024;
- // 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
- buffer = new_buffer; // update buffer information
- len = new_len;
- }
-}
-
-
-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);
- 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_buf.set_int32(time_sig.length());
- ser_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_buf.set_int32(tracks());
- ser_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);
-}
-
-
-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);
- for (j = 0; j < len; j++) {
- ser_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);
- if (event->is_note()) {
- ser_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();
- long parm_num = 0;
- ser_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);
- } 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();
- }
- // write length, not including ALGT, including padding at end
- ser_buf.store_long(length_offset, ser_buf.get_posn() - length_offset);
-}
-
-
-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();
- switch (parm->attr_type()) {
- case 'r':
- ser_buf.check_buffer(8);
- ser_buf.set_double(parm->r);
- break;
- case 's':
- ser_buf.check_buffer(strlen(parm->s) + 1);
- ser_buf.set_string(parm->s);
- break;
- case 'i':
- ser_buf.check_buffer(4);
- ser_buf.set_int32(parm->i);
- break;
- case 'l':
- ser_buf.check_buffer(4);
- ser_buf.set_int32(parm->l);
- break;
- case 'a':
- ser_buf.check_buffer(strlen(parm->a) + 1);
- ser_buf.set_string(parm->a);
- break;
- }
-}
-
-
-
-Alg_track *Alg_track::unserialize(void *buffer, long len)
-{
- assert(len > 8);
- ser_buf.init_for_read(buffer, len);
- bool alg = ser_buf.get_char() == 'A' &&
- ser_buf.get_char() == 'L' &&
- ser_buf.get_char() == 'G';
- assert(alg);
- char c = ser_buf.get_char();
- if (c == 'S') {
- Alg_seq *seq = new Alg_seq;
- seq->unserialize_seq();
- return seq;
- } else {
- assert(c == 'T');
- Alg_track *track = new Alg_track;
- track->unserialize_track();
- return track;
- }
-}
-
-
-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);
- int i;
- for (i = 0; i < beats; i++) {
- double time = ser_buf.get_double();
- double beat = ser_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);
- 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();
- time_sig.insert(beat, num, den);
- }
- long tracks_num = ser_buf.get_int32();
- ser_buf.get_pad();
- add_track(tracks_num - 1); // create tracks_num tracks
- for (i = 0; i < tracks_num; i++) {
- track(i)->unserialize_track();
- }
- // 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);
-}
-
-
-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();
- 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();
- 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();
- Alg_note *note =
- create_note(time, channel, key, pitch, loud, dur);
- note->set_selected(selected);
- long param_num = ser_buf.get_int32();
- int j;
- // this builds a list of parameters in the correct order
- // (although order shouldn't matter)
- Alg_parameters_ptr *list = ¬e->parameters;
- for (j = 0; j < param_num; j++) {
- *list = new Alg_parameters(NULL);
- unserialize_parameter(&((*list)->parm));
- list = &((*list)->next);
- }
- append(note);
- } else {
- assert(type == 'u');
- Alg_update *update = create_update(time, channel, key);
- update->set_selected(selected);
- unserialize_parameter(&(update->parameter));
- append(update);
- }
- ser_buf.get_pad();
- }
- assert(offset + bytes == ser_buf.get_posn());
-}
-
-
-void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr)
-{
- char *attr = ser_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();
- break;
- case 's':
- parm_ptr->s = heapify(ser_buf.get_string());
- break;
- case 'i':
- ser_buf.check_input_buffer(4);
- parm_ptr->i = ser_buf.get_int32();
- break;
- case 'l':
- ser_buf.check_input_buffer(4);
- parm_ptr->l = (bool) ser_buf.get_int32();
- break;
- case 'a':
- parm_ptr->a = symbol_table.insert_attribute(ser_buf.get_string());
- break;
- }
-}
-
-
-void Alg_track::set_time_map(Alg_time_map *map)
-{
- if (time_map) time_map->dereference();
- if (map == NULL) {
- time_map = new Alg_time_map(); // new default map
- time_map->reference();
- } else {
- time_map = map;
- time_map->reference();
- }
-}
-
-
-void Alg_track::convert_to_beats()
-// modify all times and durations in notes to beats
-{
- if (units_are_seconds) {
- units_are_seconds = false;
- long i;
-
- for (i = 0; i < length(); i++) {
- Alg_event_ptr e = events[i];
- double beat = time_map->time_to_beat(e->time);
- if (e->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) e;
- n->dur = time_map->time_to_beat(n->time + n->dur) - beat;
- }
- e->time = beat;
- }
- }
-}
-
-
-void Alg_track::convert_to_seconds()
-// modify all times and durations in notes to seconds
-{
- if (!units_are_seconds) {
- last_note_off = time_map->beat_to_time(last_note_off);
- units_are_seconds = true;
- long i;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr e = events[i];
- double time = time_map->beat_to_time(e->time);
- if (e->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) e;
- n->dur = time_map->beat_to_time(n->time + n->dur) - time;
- }
- e->time = time;
- }
- }
-}
-
-
-void Alg_track::set_dur(double duration)
-{
- // set beat_dur and real_dur
- if (units_are_seconds) {
- set_real_dur(duration);
- set_beat_dur(time_map->time_to_beat(duration));
- } else {
- set_beat_dur(duration);
- set_real_dur(time_map->beat_to_time(duration));
- }
-}
-
-
-Alg_note *Alg_track::create_note(double time, int channel, int identifier,
- float pitch, float loudness, double duration)
-{
- Alg_note *note = new Alg_note();
- note->time = time;
- note->chan = channel;
- note->set_identifier(identifier);
- note->pitch = pitch;
- note->loud = loudness;
- note->dur = duration;
- return note;
-}
-
-
-Alg_update *Alg_track::create_update(double time, int channel, int identifier)
-{
- Alg_update *update = new Alg_update();
- update->time = time;
- update->chan = channel;
- update->set_identifier(identifier);
- return update;
-}
-
-
-Alg_track_ptr Alg_track::cut(double t, double len, bool all)
-{
- // since we are translating notes in time, do not copy or use old timemap
- Alg_track_ptr track = new Alg_track();
- track->units_are_seconds = units_are_seconds;
- if (units_are_seconds) {
- track->set_real_dur(len);
- track->set_beat_dur(time_map->time_to_beat(t + len) -
- time_map->time_to_beat(t));
- } else {
- track->set_beat_dur(len);
- track->set_real_dur(time_map->beat_to_time(t + len) -
- time_map->beat_to_time(t));
- }
- int i;
- int new_len = 0;
- int change = 0;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- event->time -= t;
- track->append(event);
- change = 1;
- } else { // if we're not cutting this event, move it to
- // eliminate the gaps in events left by cut events
- events[new_len] = event;
- // adjust times of events after t + len
- if (event->time > t + len - ALG_EPS) {
- event->time -= len;
- change = 1;
- }
- new_len++;
- }
- }
- // Alg_event_lists based on this track become invalid
- sequence_number += change;
- this->len = new_len; // adjust length since we removed events
- return track;
-}
-
-
-Alg_track_ptr Alg_track::copy(double t, double len, bool all)
-{
- // since we are translating notes in time, do not copy or use old timemap
- Alg_track_ptr track = new Alg_track();
- track->units_are_seconds = units_are_seconds;
- if (units_are_seconds) {
- track->set_real_dur(len);
- track->set_beat_dur(time_map->time_to_beat(t + len) -
- time_map->time_to_beat(t));
- } else {
- track->set_beat_dur(len);
- track->set_real_dur(time_map->beat_to_time(t + len) -
- time_map->beat_to_time(t));
- }
- int i;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- Alg_event_ptr new_event = copy_event(event);
- new_event->time -= t;
- track->append(new_event);
- }
- }
- return track;
-}
-
-
-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;
- if (seq->get_type() == 'e') {
- assert(seq->get_owner()->get_units_are_seconds() == units_are_seconds);
- } else { // make it match
- Alg_track_ptr tr = (Alg_track_ptr) seq;
- prev_units_are_seconds = tr->get_units_are_seconds();
- if (units_are_seconds) tr->convert_to_seconds();
- else tr->convert_to_beats();
- }
- double dur = (units_are_seconds ? seq->get_real_dur() :
- seq->get_beat_dur());
-
- // Note: in the worst case, seq may contain notes
- // that start almost anytime up to it's duration,
- // so the simplest algorithm is simply a sequence
- // of inserts. If this turns out to be too slow,
- // we can do a merge sort in the case that seq
- // is an Alg_track (if it's an Alg_event_list, we
- // are not guaranteed that the events are in time
- // order, but currently, only a true seq is allowed)
-
- int i;
- for (i = 0; i < length(); i++) {
- if (events[i]->time > t - ALG_EPS) {
- events[i]->time += dur;
- }
- }
- for (i = 0; i < seq->length(); i++) {
- Alg_event *new_event = copy_event((*seq)[i]);
- new_event->time += t;
- insert(new_event);
- }
- // restore track units to what they were before
- if (seq->get_type() != 'e') {
- Alg_track_ptr tr = (Alg_track_ptr) seq;
- if (prev_units_are_seconds) tr->convert_to_seconds();
- else tr->convert_to_beats();
- }
-
-}
-
-
-void Alg_track::merge(double t, Alg_event_list_ptr seq)
-{
- Alg_event_list_ref s = *seq;
- for (int i = 0; i < s.length(); i++) {
- Alg_event *new_event;
- if (s[i]->is_note()) {
- new_event = new Alg_note((Alg_note_ptr) s[i]);
- } else {
- new_event = new Alg_update((Alg_update_ptr) s[i]);
- }
- new_event->time += t;
- insert(new_event);
- }
-}
-
-
-void Alg_track::clear(double t, double len, bool all)
-{
- int i;
- int move_to = 0;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- delete events[i];
- } else { // if we're not clearing this event, move it to
- // eliminate the gaps in events left by cleared events
- events[move_to] = event;
- // adjust times of events after t + len. This test is based
- // on the one in Alg_event::overlap() for consistency.
- if (event->time > t + len - ALG_EPS && event->time > t)
- event->time -= len;
- move_to++;
- }
- }
- if (move_to != this->len) { // we cleared at least one note
- sequence_number++; // Alg_event_lists based on this track become invalid
- }
- this->len = move_to; // adjust length since we removed events
-}
-
-
-void Alg_track::silence(double t, double len, bool all)
-{
- int i;
- int move_to = 0;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- delete events[i];
- } else { // if we're not clearing this event, move it to
- // eliminate the gaps in events left by cleared events
- events[move_to] = event;
- move_to++;
- }
- }
- if (move_to != this->len) { // we cleared at least one note
- sequence_number++; // Alg_event_lists based on this track become invalid
- }
- this->len = move_to; // adjust length since we removed events
-}
-
-
-void Alg_track::insert_silence(double t, double len)
-{
- int i;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->time > t - ALG_EPS) event->time += len;
- }
-}
-
-
-Alg_event_list *Alg_track::find(double t, double len, bool all,
- long channel_mask, long event_type_mask)
-{
- int i;
- Alg_event_list *list = new Alg_event_list(this);
- if (units_are_seconds) { // t and len are seconds
- list->set_real_dur(len);
- list->set_beat_dur(get_time_map()->time_to_beat(t + len) -
- get_time_map()->time_to_beat(t));
- } else { // t and len are beats
- list->set_real_dur(get_time_map()->beat_to_time(t + len) -
- get_time_map()->beat_to_time(t));
- list->set_beat_dur(len);
- }
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- if ((channel_mask == 0 ||
- (event->chan < 32 &&
- (channel_mask & (1 << event->chan)))) &&
- ((event_type_mask == 0 ||
- (event_type_mask & (1 << event->get_type_code()))))) {
- list->append(event);
- }
- }
- }
- return list;
-}
-
-
-void Alg_time_sigs::expand()
-{
- 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;
- time_sigs = new_time_sigs;
-}
-
-
-void Alg_time_sigs::insert(double beat, double num, double den)
-{
- // find insertion point:
- for (int i = 0; i < len; i++) {
- if (within(time_sigs[i].beat, beat, ALG_EPS)) {
- // overwrite location i with new info
- time_sigs[i].beat = beat;
- time_sigs[i].num = num;
- time_sigs[i].den = den;
- return;
- } else if (time_sigs[i].beat > beat) {
- if ((i > 0 && // check if redundant with prev. time sig
- time_sigs[i - 1].num == num &&
- time_sigs[i - 1].den == den &&
- within(fmod(beat - time_sigs[i - 1].beat,
- 4 * time_sigs[i-1].num / time_sigs[i-1].den),
- 0, ALG_EPS)) ||
- // 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
- }
- // 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;
- return;
- }
- }
- // if we fall out of loop, then this goes at end
- if (maxlen <= len) expand();
- time_sigs[len].beat = beat;
- time_sigs[len].num = num;
- time_sigs[len].den = den;
- len++;
-}
-
-
-void Alg_time_sigs::show()
-{
- printf("Alg_time_sig: ");
- for (int i = 0; i < len; i++) {
- printf("(%g: %g/%g) ", time_sigs[i].beat, time_sigs[i].num, time_sigs[i].den);
- }
- printf("\n");
-}
-
-
-int Alg_time_sigs::find_beat(double beat)
-{
- // index where you would insert a new time signature at beat
- int i = 0;
- while (i < len && time_sigs[i].beat < beat - ALG_EPS) i++;
- return i;
-}
-
-
-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;
- // 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;
- }
- // 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;
- }
- len = i_out;
-}
-
-
-void Alg_time_sigs::trim(double start, double end)
-{
- // remove time_sig's not in [start, start+end)
- // 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;
- // first, skip time signatures up to start
- i_in = find_beat(start);
- // 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.
- // If after start, then insert time sig at i_in-1 at 0.
- // Otherwise, we'll pick up time sig at i_in below.
- // If 0 == i_in < len, then the time sig at i_in is either
- // at start or after start.
- // If after start, then time sig at 0 is 4/4, but that's the
- // default, so do nothing.
- // Otherwise, we'll pick up time sig at i_in below.
- // If 0 < i_in == len, then insert time_sig at i_in-1 at start
- // If 0 == i_in == len, then 4/4 default applies and we're done.
- //
- // So the conditions for inserting time_sig[in_i-1] at 0 are:
- // (0 < i_in < len and time_sig[i] > start+ALG_EPS) OR
- // (0 < i_in == len)
- // We can rewrite this to
- // (0 < i_in) && ((i_in < len && time_sig[i_in].beat > start + ALG_EPS) ||
- // (i_in == len)))
- //
- if (0 < i_in && ((i_in < len && time_sigs[i_in].beat > start + ALG_EPS) ||
- (i_in == len))) {
- time_sigs[0] = time_sigs[i_in - 1];
- time_sigs[0].beat = 0.0;
- i_out = 1;
- }
- // scan 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;
- time_sigs[i_out] = ts;
- i_in++;
- i_out++;
- }
- len = i_out;
-}
-
-
-void Alg_time_sigs::paste(double start, Alg_seq *seq)
-{
- // printf("time_sig::insert before paste\n");
- // show();
- Alg_time_sigs &from = seq->time_sig;
- // printf("time_sig::insert from\n");
- from.show();
- // insert time signatures from seq into this time_sigs at start
- if (len == 0 && from.len == 0) {
- return; // default applies
- }
- int i = find_beat(start);
- // remember the time signature at the splice point
- double num_after_splice = 4;
- double den_after_splice = 4; // default
- // 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))) {
- num_after_splice = time_sigs[i-1].num;
- den_after_splice = time_sigs[i-1].den;
- } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) {
- num_after_splice = time_sigs[i].num;
- den_after_splice = time_sigs[i].den;
- }
- // 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();
- while (i < len) {
- time_sigs[i].beat += dur;
- i++;
- }
- //printf("time_sig::insert after making space\n");
- //show();
- // now insert initial 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
- //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);
- }
- //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);
- //printf("time_sig::insert after sig at end of splice\n");
- //show();
-}
-
-
-void Alg_time_sigs::insert_beats(double beat, double len)
-{
- 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;
- }
- }
- // now, increase beat times by len
- for (; i < len; i++) {
- time_sigs[i].beat += len;
- }
-}
-
-
-Alg_tracks::~Alg_tracks()
-{
- // Alg_events objects (track data) are not deleted, only the array
- if (tracks) {
- delete[] tracks;
- }
-}
-
-
-void Alg_tracks::expand_to(int new_max)
-{
- maxlen = new_max;
- Alg_track_ptr *new_tracks = new Alg_track_ptr[maxlen];
- // now do copy
- memcpy(new_tracks, tracks, len * sizeof(Alg_track_ptr));
- if (tracks) {
- delete[] tracks;
- }
- tracks = new_tracks;
-}
-
-
-void Alg_tracks::expand()
-{
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- expand_to(maxlen);
-}
-
-
-void Alg_tracks::append(Alg_track_ptr track)
-{
- if (maxlen <= len) {
- expand();
- }
- tracks[len] = track;
- len++;
-}
-
-
-void Alg_tracks::add_track(int track_num, Alg_time_map_ptr time_map,
- bool seconds)
- // Create a new track at index track_num.
- // If track already exists, this call does nothing.
- // If highest previous track is not at track_num-1, then
- // create tracks at len, len+1, ..., track_num.
-{
- assert(track_num >= 0);
- if (track_num == maxlen) {
- // use eponential growth to insert tracks sequentially
- expand();
- } else if (track_num > maxlen) {
- // grow to exact size for random inserts
- expand_to(track_num + 1);
- }
- if (track_num < len) return; // don't add if already there
- while (len <= track_num) {
- tracks[len] = new Alg_track(time_map, seconds);
- //printf("allocated track at %d (%x, this %x) = %x\n", len,
- // &(tracks[len]), this, tracks[len]);
- len++;
- }
-}
-
-
-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++) {
- delete tracks[i];
- }
- if (tracks) delete [] tracks;
- tracks = NULL;
- len = 0;
- maxlen = 0; // Modified by Ning Hu Nov.19 2002
-}
-
-
-Alg_seq::Alg_seq(const char *filename, bool smf)
-{
- basic_initialization();
- ifstream inf(filename, smf ? ios::binary | ios::in : ios::in);
- if (inf.fail()) {
- error = alg_error_open;
- return;
- }
- if (smf) {
- error = alg_smf_read(inf, this);
- } else {
- error = alg_read(inf, this);
- }
- inf.close();
-}
-
-
-Alg_seq::Alg_seq(istream &file, bool smf)
-{
- basic_initialization();
- if (smf) {
- error = alg_smf_read(file, this);
- } else {
- error = alg_read(file, this);
- }
-}
-
-void Alg_seq::seq_from_track(Alg_track_ref tr)
-{
- type = 's';
- // copy everything
- set_beat_dur(tr.get_beat_dur());
- set_real_dur(tr.get_real_dur());
- // copy time_map
- set_time_map(new Alg_time_map(tr.get_time_map()));
- units_are_seconds = tr.get_units_are_seconds();
-
- if (tr.get_type() == 's') {
- Alg_seq_ref s = *(tr.to_alg_seq());
- channel_offset_per_track = s.channel_offset_per_track;
- add_track(s.tracks() - 1);
- // copy each track
- for (int i = 0; i < tracks(); i++) {
- Alg_track_ref from_track = *(s.track(i));
- Alg_track_ref to_track = *(track(i));
- to_track.set_beat_dur(from_track.get_beat_dur());
- to_track.set_real_dur(from_track.get_real_dur());
- if (from_track.get_units_are_seconds())
- to_track.convert_to_seconds();
- for (int j = 0; j < from_track.length(); j++) {
- Alg_event_ptr event = copy_event(from_track[j]);
- to_track.append(event);
- }
- }
- } else if (tr.get_type() == 't') {
- add_track(0);
- channel_offset_per_track = 0;
- Alg_track_ptr to_track = track(0);
- to_track->set_beat_dur(tr.get_beat_dur());
- to_track->set_real_dur(tr.get_real_dur());
- for (int j = 0; j < tr.length(); j++) {
- Alg_event_ptr event = copy_event(tr[j]);
- to_track->append(event);
- }
- } else {
- assert(false); // expected track or sequence
- }
-}
-
-
-int Alg_seq::tracks()
-{
- return track_list.length();
-}
-
-
-Alg_track_ptr Alg_seq::track(int i)
-{
- assert(0 <= i && i < track_list.length());
- return &(track_list[i]);
-}
-
-
-#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();
- int tr = 0;
- while (tr < ntracks) {
- Alg_track *a_track = track(tr);
- if (a_track && i < a_track->length()) {
- return (*a_track)[i];
- } else if (a_track) {
- i -= a_track->length();
- }
- tr++;
- }
- assert(false); // out of bounds
-}
-
-
-void Alg_seq::convert_to_beats()
-{
- if (!units_are_seconds) return;
- for (int i = 0; i < tracks(); i++) {
- track(i)->convert_to_beats();
- }
- // note that the Alg_seq inherits units_are_seconds from an
- // empty track. Each track also has a (redundant) field called
- // units are seconds. These should always be consistent.
- units_are_seconds = false;
-}
-
-
-void Alg_seq::convert_to_seconds()
-{
- if (units_are_seconds) return;
- //printf("convert_to_seconds, tracks %d\n", tracks());
- //printf("last_tempo of seq: %g on map %x\n",
- // get_time_map()->last_tempo, get_time_map());
- for (int i = 0; i < tracks(); i++) {
- //printf("last_tempo of track %d: %g on %x\n", i,
- // track(i)->get_time_map()->last_tempo,
- // track(i)->get_time_map());
- track(i)->convert_to_seconds();
- }
- // update our copy of last_note_off (which may or may not be valid)
- last_note_off = time_map->beat_to_time(last_note_off);
- // note that the Alg_seq inherits units_are_seconds from an
- // empty track. Each track also has a (redundant) field called
- // units are seconds. These should always be consistent.
- units_are_seconds = true;
-}
-
-
-Alg_track_ptr Alg_seq::cut_from_track(int track_num, double start,
- double dur, bool all)
-{
- assert(track_num >= 0 && track_num < tracks());
- Alg_track_ptr tr = track(track_num);
- return tr->cut(start, dur, all);
-}
-
-
-void Alg_seq::copy_time_sigs_to(Alg_seq *dest)
-{
- // copy time signatures
- for (int i = 0; i < time_sig.length(); i++) {
- dest->time_sig.insert(time_sig[i].beat, time_sig[i].num,
- time_sig[i].den);
- }
-}
-
-
-void Alg_seq::set_time_map(Alg_time_map *map)
-{
- Alg_track::set_time_map(map);
- for (int i = 0; i < tracks(); i++) {
- track(i)->set_time_map(map);
- }
-}
-
-
-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
-{
- // fix parameters to fall within existing sequence
- if (start > get_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;
-
- Alg_seq_ptr result = new Alg_seq();
- Alg_time_map_ptr map = new Alg_time_map(get_time_map());
- result->set_time_map(map);
- copy_time_sigs_to(result);
- result->units_are_seconds = units_are_seconds;
- result->track_list.reset();
-
- for (int i = 0; i < tracks(); i++) {
- Alg_track_ptr cut_track = cut_from_track(i, start, len, all);
- result->track_list.append(cut_track);
- // initially, result->last_note_off is zero. We want to know the
- // maximum over all cut_tracks, so compute that here:
- result->last_note_off = MAX(result->last_note_off,
- cut_track->last_note_off);
- // since we're moving to a new sequence, change the track's time_map
- result->track_list[i].set_time_map(map);
- }
-
- // put units in beats to match time_sig's. Note that we need
- // two different end times. For result, we want the time of the
- // last note off, but for cutting out the time signatures in this,
- // we use len.
- double ts_start = start;
- double ts_end = start + len;
- 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);
- }
- // result is shifted from start to 0 and has length len, but
- // time_sig and time_map are copies from this. Adjust time_sig,
- // time_map, and duration fields in result. The time_sig and
- // time_map data is retained out to last_note_off so that we have
- // information for the entire duration of all the notes, even though
- // this might extend beyond the duration of the track. (Warning:
- // no info is retained for notes with negative times.)
- result->time_sig.trim(ts_start, ts_last_note_off);
- result->time_map->trim(start, start + result->last_note_off,
- result->units_are_seconds);
- // even though there might be notes sticking out beyond len, the
- // track duration is len, not last_note_off. (Warning: if all is
- // true, there may also be notes at negative offsets. These times
- // cannot be mapped between beat and time representations, so there
- // may be subtle bugs or unexpected behaviors in that case.)
- result->set_dur(len);
-
- // 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_map->cut(start, len, units_are_seconds);
- set_dur(get_dur() - len);
-
- return result;
-}
-
-
-void Alg_seq::insert_silence_in_track(int track_num, double t, double len)
-{
- Alg_track_ptr tr = track(track_num);
- tr->insert_silence(t, len);
-}
-
-
-void Alg_seq::insert_silence(double t, double len)
-{
- for (int i = 0; i < tracks(); i++) {
- insert_silence_in_track(i, t, len);
- }
- double t_beats = t;
- double len_beats = len;
- // insert into time_sig array; use time_sig_paste,
- // which requires us to build a simple time_sig array
- if (units_are_seconds) {
- time_map->insert_time(t, len);
- t_beats = time_map->time_to_beat(t);
- len_beats = time_map->time_to_beat(t + len) - t_beats;
- } else {
- time_map->insert_beats(t_beats, len_beats);
- }
- if (time_sig.length() > 0) {
- time_sig.insert_beats(t_beats, len_beats);
- }
-}
-
-
-Alg_track_ptr Alg_seq::copy_track(int track_num, double t, double len, bool all)
-{
- return track_list[track_num].copy(t, len, all);
-}
-
-
-Alg_seq *Alg_seq::copy(double start, double len, bool all)
-{
- // fix parameters to fall within existing sequence
- if (start > get_dur()) return NULL; // nothing to copy
- if (start < 0) start = 0; // can't copy before sequence starts
- if (start + len > get_dur()) // can't copy after end:
- len = get_dur() - start;
-
- // return (new) sequence from start to start + len
- Alg_seq_ptr result = new Alg_seq();
- Alg_time_map_ptr map = new Alg_time_map(get_time_map());
- result->set_time_map(map);
- copy_time_sigs_to(result);
- result->units_are_seconds = units_are_seconds;
- result->track_list.reset();
-
- for (int i = 0; i < tracks(); i++) {
- Alg_track_ptr copy = copy_track(i, start, len, all);
- result->track_list.append(copy);
- result->last_note_off = MAX(result->last_note_off,
- copy->last_note_off);
- // since we're copying to a new seq, change the track's time_map
- result->track_list[i].set_time_map(map);
- }
-
- // put units in beats to match time_sig's. Note that we need
- // two different end times. For result, we want the time of the
- // last note off, but for cutting out the time signatures in this,
- // we use len.
- double ts_start = start;
- double ts_end = start + len;
- 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);
- }
-
- result->time_sig.trim(ts_start, ts_last_note_off);
- result->time_map->trim(start, start + result->last_note_off,
- units_are_seconds);
- result->set_dur(len);
- return result;
-}
-
-
-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
- bool units_should_be_seconds = units_are_seconds;
- bool seq_units_should_be_seconds = seq->get_units_are_seconds();
- if (units_are_seconds) {
- start = time_map->time_to_beat(start);
- convert_to_beats();
- }
- seq->convert_to_beats();
-
- // do a paste on each track
- int i;
- for (i = 0; i < seq->tracks(); i++) {
- if (i >= tracks()) {
- add_track(i);
- }
- track(i)->paste(start, seq->track(i));
- }
- // make sure all tracks were opened up for an insert, even if
- // there is nothing to insert
- while (i < tracks()) {
- track(i)->insert_silence(start, seq->get_dur());
- i++;
- }
- // paste in tempo track
- time_map->paste(start, seq);
- // paste in time signatures
- time_sig.paste(start, seq);
- set_dur(get_beat_dur() + seq->get_dur());
- assert(!seq->units_are_seconds && !units_are_seconds);
- if (units_should_be_seconds) {
- convert_to_seconds();
- }
- if (seq_units_should_be_seconds) {
- seq->convert_to_seconds();
- }
-}
-
-
-void Alg_seq::merge(double t, Alg_event_list_ptr seq)
-{
- // seq must be an Alg_seq:
- assert(seq->get_type() == 's');
- Alg_seq_ptr s = (Alg_seq_ptr) seq;
- for (int i = 0; i < s->tracks(); i++) {
- if (tracks() <= i) add_track(i);
- track(i)->merge(t, s->track(i));
- }
-}
-
-
-void Alg_seq::silence_track(int track_num, double start, double len, bool all)
-{
- // remove events in [time, time + len) and close gap
- Alg_track_ptr tr = track(track_num);
- tr->silence(start, len, all);
-}
-
-
-void Alg_seq::silence(double t, double len, bool all)
-{
- for (int i = 0; i < tracks(); i++) {
- silence_track(i, t, len, all);
- }
-}
-
-
-void Alg_seq::clear_track(int track_num, double start, double len, bool all)
-{
- // remove events in [time, time + len) and close gap
- Alg_track_ptr tr = track(track_num);
- tr->clear(start, len, 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
- 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;
-
- for (int i = 0; i < tracks(); i++)
- clear_track(i, start, len, all);
-
- // Put units in beats to match time_sig's.
- double ts_start = start;
- double ts_end = start + len;
- if (units_are_seconds) {
- ts_start = time_map->time_to_beat(ts_start);
- ts_end = time_map->time_to_beat(ts_end);
- }
-
- // 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_map->cut(start, len, units_are_seconds);
- set_dur(get_dur() - len);
-}
-
-
-Alg_event_list_ptr Alg_seq::find_in_track(int track_num, double t, double len,
- bool all, long channel_mask,
- long event_type_mask)
-{
- return track(track_num)->find(t, len, all, channel_mask, event_type_mask);
-}
-
-
-Alg_seq::~Alg_seq()
-{
- int i, j;
- // Tracks does not delete Alg_events elements
- for (j = 0; j < track_list.length(); j++) {
- Alg_track ¬es = track_list[j];
- // Alg_events does not delete notes
- for (i = 0; i < notes.length(); i++) {
- Alg_event_ptr event = notes[i];
- delete event;
- }
- }
-}
-
-
-long Alg_seq::seek_time(double time, int track_num)
-// find index of first score event after time
-{
- long i;
- Alg_events ¬es = track_list[track_num];
- for (i = 0; i < notes.length(); i++) {
- if (notes[i]->time > time) {
- break;
- }
- }
- return i;
-}
-
-
-bool Alg_seq::insert_beat(double time, double beat)
-// insert a time,beat pair
-// return true or false (false indicates an error, no update)
-// it is an error to imply a negative tempo or to insert at
-// a negative time
-{
- if (time < 0 || beat < 0) return false;
- if (time == 0.0 && beat > 0)
- time = 0.000001; // avoid infinite tempo, offset time by 1us
- if (time == 0.0 && beat == 0.0)
- return true; // (0,0) is already in the map!
- convert_to_beats(); // beats are invariant when changing tempo
- time_map->insert_beat(time, beat);
- return true;
-}
-
-
-bool Alg_seq::insert_tempo(double bpm, double beat)
-{
- double bps = bpm / 60.0; // convert to beats per second
- // change the tempo at the given beat until the next beat event
- if (beat < 0) return false;
- convert_to_beats(); // beats are invariant when changing tempo
- double time = time_map->beat_to_time(beat);
- long i = time_map->locate_time(time);
- if (i >= time_map->beats.len || !within(time_map->beats[i].time, time, 0.000001)) {
- insert_beat(time, beat);
- }
- // now i is index of beat where tempo will change
- if (i == time_map->beats.len - 1) {
- time_map->last_tempo = bps;
- time_map->last_tempo_flag = true;
- } else { // adjust all future beats
- // compute the difference in beats
- double diff = time_map->beats[i + 1].beat - time_map->beats[i].beat;
- // convert beat difference to seconds at new tempo
- diff = diff / bps;
- // figure out old time difference:
- double old_diff = time_map->beats[i + 1].time - time;
- // compute difference too
- diff = diff - old_diff;
- // apply new_diff to score and beats
- while (i < time_map->beats.len) {
- time_map->beats[i].time = time_map->beats[i].time + diff;
- i++;
- }
- }
- return true;
-}
-
-
-void Alg_seq::add_event(Alg_event_ptr event, int track_num)
- // add_event puts an event in a given track (track_num).
- // The track must exist. The time and duration of the
- // event are interpreted according to whether the Alg_seq
- // is currently in beats or seconds (see convert_to_beats())
-{
- track_list[track_num].insert(event);
-/*
- if (event->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) event;
- trace("note %d at %g for %g\n", n->get_identifier(), n->time, n->dur);
- }
- */
-}
-
-
-bool Alg_seq::set_tempo(double bpm, double start_beat, double end_beat)
-// set tempo from start_beat to end_beat
-{
- // this is an optimization, the test is repeated in Alg_time_seq::set_tempo()
- if (start_beat >= end_beat) return false;
- bool units_should_be_seconds = units_are_seconds;
- convert_to_beats();
- bool result = time_map->set_tempo(bpm, start_beat, end_beat);
- if (units_should_be_seconds) convert_to_seconds();
- return result;
-}
-
-
-void Alg_seq::set_time_sig(double beat, double num, double den)
-{
- time_sig.insert(beat, num, den);
-}
-
-
-void Alg_seq::beat_to_measure(double beat, long *measure, double *m_beat,
- double *num, double *den)
-{
- // return [measure, beat, num, den]
- double m = 0; // measure number
- double bpm;
- int tsx;
- bpm = 4;
- // assume 4/4 if no time signature
- double prev_beat = 0;
- double prev_num = 4;
- double prev_den = 4;
-
- if (beat < 0) beat = 0; // negative measures treated as zero
-
- for (tsx = 0; tsx < time_sig.length(); tsx++) {
- if (time_sig[tsx].beat <= beat) {
- // round m up to an integer (but allow for a small
- // numerical inaccuracy)
- m = m + (long) (0.99 + (time_sig[tsx].beat - prev_beat) / bpm);
- bpm = time_sig[tsx].num * 4 / time_sig[tsx].den;
- prev_beat = time_sig[tsx].beat;
- prev_num = time_sig[tsx].num;
- prev_den = time_sig[tsx].den;
- } else {
- m = m + (beat - prev_beat) / bpm;
- *measure = (long) m;
- *m_beat = (m - *measure) * bpm;
- *num = prev_num;
- *den = prev_den;
- return;
- }
- }
- // if we didn't return yet, compute after last time signature
- Alg_time_sig initial(0, 4, 4);
- Alg_time_sig &prev = initial;
- if (tsx > 0) { // use last time signature
- prev = time_sig[time_sig.length() - 1];
- }
- bpm = prev.num * 4 / prev.den;
- m = m + (beat - prev.beat) / bpm;
- *measure = (long) m;
- *m_beat = (m - *measure) * bpm;
- *num = prev.num;
- *den = prev.den;
-}
-
-/*
-void Alg_seq::set_events(Alg_event_ptr *events, long len, long max)
-{
- convert_to_seconds(); // because notes are in seconds
- notes.set_events(events, len, max);
-}
-*/
-
-
-void Alg_seq::iteration_begin()
-{
- // keep an array of indexes into tracks
- current = new long[track_list.length()];
- int i;
- for (i = 0; i < track_list.length(); i++) {
- current[i] = 0;
- }
-}
-
-
-Alg_event_ptr Alg_seq::iteration_next()
- // 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 {
- return NULL;
- }
-}
-
-
-void Alg_seq::iteration_end()
-{
- delete[] current;
-}
-
-
-void Alg_seq::merge_tracks()
-{
- long sum = 0;
- long i;
- for (i = 0; i < track_list.length(); i++) {
- sum = sum + track(i)->length();
- }
- // preallocate array for efficiency:
- Alg_event_ptr *notes = new Alg_event_ptr[sum];
- iteration_begin();
- long notes_index = 0;
-
- Alg_event_ptr event;
- while (( event = iteration_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();
-}
-
-
-// sr_letter_to_type = {"i": 'Integer', "r": 'Real', "s": 'String',
-// "l": 'Logical', "a": 'Symbol'}
-
-
diff --git a/plugins/MidiImport/portsmf/allegro.h b/plugins/MidiImport/portsmf/allegro.h
deleted file mode 100644
index e83d4b46375..00000000000
--- a/plugins/MidiImport/portsmf/allegro.h
+++ /dev/null
@@ -1,945 +0,0 @@
-// Portsmf (also known as Allegro):
-// music representation system, with
-// extensible in-memory sequence structure
-// upward compatible with MIDI
-// implementations in C++ and Serpent
-// external, text-based representation
-// compatible with Aura
-//
-// SERIALBUFFER CLASS
-//
-// The Serial_buffer class is defined to support serialization and
-// unserialization. A Serial_buffer is just a block of memory with
-// a length and a read/write pointer. When writing, it can expand.
-//
-// SERIALIZATION
-//
-// The Alg_track class has static members:
-// ser_buf -- a Serial_buffer
-// When objects are serialized, they are first written to
-// ser_buf, which is expanded whenever necessary. Then, when
-// the length is known, new memory is allocated and the data
-// is copied to a correctly-sized buffer and returned to caller.
-// The "external" (callable from outside the library)
-// serialization functions are:
-// Alg_track::serialize()
-// Alg_seq::serialize()
-// The "internal" serialization functions to be called from within
-// the library are:
-// Alg_track::serialize_track(bool text)
-// Alg_seq::serialize_seq(bool text)
-// Alg_track::serialize_parameter(
-// Alg_parameter *parm, bool text)
-// These internal serialize functions append data to ser_buf The text
-// flag says to write an ascii representation as opposed to binary.
-//
-// UNSERIALIZATION:
-//
-// The Alg_track class has a static member:
-// unserialize(char *buffer, long len)
-// that will unserialize anything -- an Alg_track or an Alg_seq.
-// No other function should be called from outside the library.
-// Internal unserialize functions are:
-// Alg_seq::unserialize_seq()
-// Alg_track::unserialize_track()
-// Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr)
-// 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"
-
-#define ALG_EPS 0.000001 // epsilon
-#define ALG_DEFAULT_BPM 100.0 // default tempo
-
-// are d1 and d2 within epsilon of each other?
-bool within(double d1, double d2, double epsilon);
-
-char *heapify(const char *s); // put a string on the heap
-
-
-// Alg_attribute is an atom in the symbol table
-// with the special addition that the last
-// character is prefixed to the string; thus,
-// the attribute 'tempor' (a real) is stored
-// as 'rtempor'. To get the string name, just
-// use attribute+1.
-typedef char *Alg_attribute;
-#define alg_attr_name(a) ((a) + 1)
-#define alg_attr_type(a) (*(a))
-
-// Alg_atoms is a symbol table of Alg_attributes and other
-// unique strings
-class Alg_atoms {
-public:
- Alg_atoms() {
- maxlen = len = 0;
- atoms = NULL;
- }
- // insert/lookup an atttribute
- Alg_attribute insert_attribute(Alg_attribute attr);
- // insert/lookup attribute by name (without prefixed type)
- Alg_attribute insert_string(const char *name);
-private:
- long maxlen;
- long len;
- char **atoms;
-
- // insert an Attriubute not in table after moving attr to heap
- Alg_attribute insert_new(const char *name, char attr_type);
- void expand(); // make more space
-};
-
-extern Alg_atoms symbol_table;
-
-
-// an attribute/value pair. Since Alg_attribute names imply type,
-// we try to keep attributes and values packaged together as
-// Alg_parameter class
-typedef class Alg_parameter {
-public:
- ~Alg_parameter();
- Alg_attribute attr;
- union {
- double r;// real
- char *s; // string
- long i; // integer
- bool l; // logical
- char *a; // symbol (atom)
- }; // anonymous union
- void copy(Alg_parameter *); // copy from another parameter
- char attr_type() { return alg_attr_type(attr); }
- char *attr_name() { return alg_attr_name(attr); }
- void set_attr(Alg_attribute a) { attr = a; }
- void show();
-} *Alg_parameter_ptr;
-
-
-// a list of attribute/value pairs
-typedef class Alg_parameters {
-public:
- class Alg_parameters *next;
- Alg_parameter parm;
-
- Alg_parameters(Alg_parameters *list) {
- next = list;
- }
-
- //~Alg_parameters() { }
-
- // each of these routines takes address of pointer to the list
- // insertion is performed without checking whether or not a
- // parameter already exists with this attribute. See find() and
- // remove_key() to assist in checking for and removing existing
- // parameters.
- // Note also that these insert_* methods convert name to an
- // 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);
- // 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 Alg_parameters *remove_key(Alg_parameters **list, const char *name);
- // find an attribute/value pair
- Alg_parameter_ptr find(Alg_attribute *attr);
-} *Alg_parameters_ptr;
-
-
-// these are type codes associated with certain attributes
-// see Alg_track::find() where these are bit positions in event_type_mask
-#define ALG_NOTE 0 // This is a note, not an update
-#define ALG_GATE 1 // "gate"
-#define ALG_BEND 2 // "bend"
-#define ALG_CONTROL 3 // "control"
-#define ALG_PROGRAM 4 // "program"
-#define ALG_PRESSURE 5 // "pressure"
-#define ALG_KEYSIG 6 // "keysig"
-#define ALG_TIMESIG_NUM 7 // "timesig_num"
-#define ALG_TIMESIG_DEN 8 // "timesig_den"
-#define ALG_OTHER 9 // any other value
-
-// abstract superclass of Alg_note and Alg_update:
-typedef class Alg_event {
-protected:
- bool selected;
- char type; // 'e' event, 'n' note, 'u' update
- long key; // note identifier
- static const char* description; // static buffer for debugging (in Alg_event)
-public:
- double time;
- long chan;
- virtual void show() = 0;
- // Note: there is no Alg_event() because Alg_event is an abstract class.
- bool is_note() { return (type == 'n'); } // tell whether an Alg_event is a note
- bool is_update() { return (type == 'u'); } // tell whether an Alg_event is a parameter update
- char get_type() { return type; } // return 'n' for note, 'u' for update
- int get_type_code(); // 1 = volume change, 2 = pitch bend,
- // 3 = control change, 4 = program change,
- // 5 = pressure change, 6 = key signature,
- // 7 = time sig numerator, 8 = time sig denominator
- bool get_selected() { return selected; }
- void set_selected(bool b) { selected = b; }
- // Note: notes are identified by a (channel, identifier) pair.
- // For midi, the identifier is the key number (pitch). The identifier
- // does not have to represent pitch; it's main purpose is to identify
- // notes so that they can be named by subsequent update events.
- long get_identifier() { return key; } // get MIDI key or note identifier of note or update
- void set_identifier(long i) { key = i; } // set the identifier
- // In all of these set_ methods, strings are owned by the caller and
- // copied as necessary by the callee. For notes, an attribute/value
- // pair is added to the parameters list. For updates, the single
- // attribute/value parameter pair is overwritten. In all cases, the
- // 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);
-
- // Some note methods. These fail (via assert()) if this is not a note:
- //
- float get_pitch();// get pitch in steps -- use this even for MIDI
- float get_loud(); // get loudness (MIDI velocity)
- // times are in seconds or beats, depending upon the units_are_seconds
- // flag in the containing sequence
- double get_start_time(); // get start time in seconds or beats
- double get_end_time(); // get end time in seconds or beats
- double get_duration(); // get duration in seconds or beats
- void set_pitch(float);
- void set_loud(float);
- void set_duration(double);
-
- // Notes have lists of attribute values. Attributes are converted
- // to/from strings in this API to avoid explicit use of Alg_attribute
- // 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:
- // '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
- // (ignore if no matching attribute/value pair exists)
-
- // Some attribute/value methods. These fail if this is not an update.
- // Attributes are converted to/from strings to avoid explicit use
- // of Alg_attribute types.
- //
- const char *get_attribute(); // get the update's attribute (string)
- 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
- // 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
- // Notes: Caller does not own the return value. Do not modify.
- // The return value's lifetime is forever.
-
- // Auxiliary function to aid in editing tracks
- // Returns true if the event overlaps the given region
- bool overlap(double t, double len, bool all);
-
- const char *GetDescription(); // computes a text description of this event
- // the result is in a static buffer, not thread-safe, just for debugging.
- Alg_event() { selected = false; }
- virtual ~Alg_event() {}
-} *Alg_event_ptr;
-
-
-typedef class Alg_note : public Alg_event {
-public:
- virtual ~Alg_note();
- Alg_note(Alg_note *); // copy constructor
- float pitch; // pitch in semitones (69 = A440)
- float loud; // dynamic corresponding to MIDI velocity
- double dur; // duration in seconds (normally to release point)
- Alg_parameters_ptr parameters; // attribute/value pair list
- Alg_note() { type = 'n'; parameters = NULL; }
- void show();
-} *Alg_note_ptr;
-
-
-typedef class Alg_update : public Alg_event {
-public:
- virtual ~Alg_update() {};
- Alg_update(Alg_update *); // copy constructor
- Alg_parameter parameter; // an update contains one attr/value pair
-
-
- Alg_update() { type = 'u'; }
- void show();
-} *Alg_update_ptr;
-
-
-// a sequence of Alg_event objects
-typedef class Alg_events {
-private:
- long maxlen;
- void expand();
-protected:
- long len;
- Alg_event_ptr *events; // events is array of pointers
-public:
- // sometimes, it is nice to have the time of the last note-off.
- // In the current implementation,
- // this field is set by append to indicate the time of the
- // last note-off in the current unit, so it should be correct after
- // creating a new track and adding notes to it. It is *not*
- // updated after uninsert(), so use it with care.
- double last_note_off;
- virtual int length() { return len; }
- Alg_event_ptr &operator[](int i) {
- assert(i >= 0 && i < len);
- return events[i];
- }
- Alg_events() {
- maxlen = len = 0;
- events = NULL;
- last_note_off = 0;
- }
- // destructor deletes the events array, but not the
- // events themselves
- ~Alg_events();
- void set_events(Alg_event_ptr *e, long l, long m) {
- if (events) delete [] events;
- events = e; len = l; maxlen = m; }
- // for use by Alg_track and Alg_seq
- void insert(Alg_event_ptr event);
- void append(Alg_event_ptr event);
- Alg_event_ptr uninsert(long index);
-} *Alg_events_ptr;
-
-class Alg_track;
-
-typedef class Alg_event_list : public Alg_events {
-protected:
- char type; // 'e' Alg_event_list, 't' Alg_track, 's' Alg_seq
- static const char *last_error_message;
- Alg_track *events_owner; // if this is an Alg_event_list,
- // the events are owned by an Alg_track or an Alg_seq
- static int sequences; // to keep track of sequence numbers
- int sequence_number; // this sequence number is incremented
- // whenever an edit is performed on an Alg_track or Alg_seq.
- // When an Alg_event_list is created to contain pointers to
- // a subset of an Alg_track or Alg_seq (the events_owner),
- // the Alg_event_list gets a copy of the events_owner's
- // sequence_number. If the events_owner is edited, the pointers
- // in this Alg_event_list will become invalid. This is detected
- // (for debugging) as differing sequence_numbers.
-
- // every event list, track, and seq has a duration.
- // Usually the duration is set when the list is constructed, e.g.
- // when you extract from 10 to 15 seconds, the duration is 5 secs.
- // The duration does not tell you when is the last note-off.
- // duration is recorded in both beats and seconds:
- double beat_dur;
- double real_dur;
-public:
- // the client should not create one of these, but these are
- // returned from various track and seq operations. An
- // Alg_event_list "knows" the Alg_track or Alg_seq that "owns"
- // the events. All events in an Alg_event_list must belong
- // to the same Alg_track or Alg_seq structure.
- // 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);
- 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);
-
- char get_type() { return type; }
- Alg_track *get_owner() { return events_owner; }
-
- // The destructor does not free events because they are owned
- // by a track or seq structure.
- virtual ~Alg_event_list();
-
- // Returns the duration of the sequence in beats or seconds
- double get_beat_dur() { return beat_dur; }
- void set_beat_dur(double d) { beat_dur = d; }
- double get_real_dur() { return real_dur; }
- void set_real_dur(double d) { real_dur = d; }
-
- // Events are stored in time order, so when you change the time of
- // an event, you must adjust the position. When you call set_start_time
- // on an Alg_event_list, the Alg_event_list is not modified, but the
- // Alg_track that "owns" the event is modified. If the owner is an
- // Alg_seq, this may require searching the seq for the track containing
- // the event. This will mean a logN search of every track in the seq
- // (but if this turns out to be a problem, we can store each event's
- // track owner in the Alg_event_list.)
- virtual void set_start_time(Alg_event *event, double);
- // get text description of run-time errors detected, clear error
- const char *get_last_error_message() { return last_error_message; }
- // Implementation hint: keep a sequence number on each Alg_track that is
- // incremented anytime there is a structural change. (This behavior is
- // inherited by Alg_seq as well.) Copy the sequence number to any
- // Alg_event_list object when it is created. Whenever you access an
- // Alg_event_list, using operator[], assert that the Alg_event_list sequence
- // number matches the Alg_seq sequence number. This will guarantee that you
- // do not try to retain pointers to events beyond the point where the events
- // may no longer exist.
-} *Alg_event_list_ptr, &Alg_event_list_ref;
-
-
-// Alg_beat is used to contruct a tempo map
-typedef class Alg_beat {
-public:
- Alg_beat(double t, double b) {
- time = t; beat = b; }
- Alg_beat() {};
- double time;
- double beat;
-} *Alg_beat_ptr;
-
-
-// Alg_beats is a list of Alg_beat objects used in Alg_seq
-typedef class Alg_beats {
-private:
- long maxlen;
- void expand();
-public:
- long len;
- Alg_beat_ptr beats;
- Alg_beat &operator[](int i) {
- assert(i >= 0 && i < len);
- return beats[i];
- }
- Alg_beats() {
- maxlen = len = 0;
- beats = NULL;
- expand();
- beats[0].time = 0;
- beats[0].beat = 0;
- len = 1;
- }
- ~Alg_beats() {
- if (beats) delete[] beats;
- }
- void insert(long i, Alg_beat_ptr beat);
-} *Alg_beats_ptr;
-
-
-typedef class Alg_time_map {
-private:
- int refcount;
-public:
- Alg_beats beats; // array of Alg_beat
- double last_tempo;
- bool last_tempo_flag;
- Alg_time_map() {
- last_tempo = ALG_DEFAULT_BPM / 60.0; // note: this value ignored until
- // last_tempo_flag is set; nevertheless, the default
- // tempo is 100.
- last_tempo_flag = true;
- refcount = 0;
- }
- Alg_time_map(Alg_time_map *map); // copy constructor
- long length() { return beats.len; }
- void show();
- long locate_time(double time);
- long locate_beat(double beat);
- double beat_to_time(double beat);
- double time_to_beat(double time);
- // Time map manipulations: it is prefered to call the corresponding
- // methods in Alg_seq. If you manipulate an Alg_time_map directly,
- // you should take care to convert all tracks that use the time map
- // to beats or seconds as appropriate: Normally if you insert a beat
- // you want tracks to be in time units and if you insert a tempo change
- // 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
- // set the tempo over a region
- bool set_tempo(double tempo, double start_beat, double end_beat);
- 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);
- // insert a span of time. If start is at a tempo change, then
- // the span of time runs at the changed tempo
- void insert_time(double start, double len);
- // insert a span of beats. If start is at a tempo change, the
- // tempo change takes effect before the inserted beats
- void insert_beats(double start, double len);
- void dereference() {
- if (--refcount <= 0) delete this;
- }
- void reference() {
- refcount++;
- }
-} *Alg_time_map_ptr;
-
-
-typedef class Serial_buffer {
-private:
- char *buffer;
- char *ptr;
- long len;
-public:
- Serial_buffer() {
- buffer = NULL;
- ptr = NULL;
- len = 0;
- }
- void init_for_write() { ptr = buffer; }
- long get_posn() { return (long) (ptr - buffer); }
- long get_len() { return len; }
- // store_long writes a long at a given offset
- void store_long(long offset, long value) {
- assert(offset <= get_posn() - 4);
- long *loc = (long *) (buffer + offset);
- *loc = value;
- }
- void check_buffer(long needed);
- void set_string(char *s) {
- char *fence = buffer + len;
- assert(ptr < fence);
- while ((*ptr++ = *s++)) assert(ptr < fence);
- // assert((char *)(((long) (ptr + 7)) & ~7) <= fence);
- pad(); }
- void set_int32(long v) { *((long *) ptr) = v; ptr += 4; }
- void set_double(double v) { *((double *) ptr) = v; ptr += 8; }
- void set_float(float v) { *((float *) ptr) = v; ptr += 4; }
- void set_char(char v) { *ptr++ = v; }
-#ifdef LMMS_BUILD_WIN64
- void pad() { while (((long long) ptr) & 7) set_char(0); }
-#else
- void pad() { while (((long) ptr) & 7) set_char(0); }
-#endif
- void *to_heap(long *len) {
- *len = get_posn();
- char *newbuf = new char[*len];
- 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;
-
-typedef class Alg_seq *Alg_seq_ptr;
-
-typedef class Alg_track : public Alg_event_list {
-protected:
- Alg_time_map *time_map;
- bool units_are_seconds;
- char *get_string(char **p, long *b);
- 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;
- 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
- void unserialize_parameter(Alg_parameter_ptr parm_ptr);
-public:
- void serialize_track();
- void unserialize_track();
- virtual Alg_event_ptr &operator[](int i) {
- assert(i >= 0 && i < len);
- return events[i];
- }
- Alg_track() { units_are_seconds = false; time_map = NULL;
- set_time_map(NULL); type = 't'; }
- // initialize empty track with a time map
- Alg_track(Alg_time_map *map, bool seconds);
-
- Alg_event_ptr copy_event(Alg_event_ptr event); // make a complete copy
-
- Alg_track(Alg_track &track); // copy constructor, does not copy time_map
- // 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); }
-
- // Returns a buffer containing a serialization of the
- // file. It will be an ASCII representation unless text is true.
- // *buffer gets a newly allocated buffer pointer. The caller must free it.
- // *len gets the length of the serialized track
- virtual void serialize(void **buffer, long *bytes);
-
- // Try to read from a memory buffer. Automatically guess
- // whether it's MIDI or text.
- static Alg_track *unserialize(void *buffer, long len);
-
- // If the track is really an Alg_seq and you need to access an
- // Alg_seq method, coerce to an Alg_seq with this function:
- Alg_seq_ptr to_alg_seq() {
- return (get_type() == 's' ? (Alg_seq_ptr) this : NULL); }
-
- // Are we using beats or seconds?
- bool get_units_are_seconds() { return units_are_seconds; }
- // Change units
- virtual void convert_to_beats();
- virtual void convert_to_seconds();
- void set_dur(double dur);
- double get_dur() { return (units_are_seconds ? real_dur : beat_dur); }
-
- // Every Alg_track may have an associated time_map. If no map is
- // specified, or if you set_time_map(NULL), then the behavior
- // should be as if there is a constant tempo of 100 beats/minute
- // (this constant is determined by ALG_DEFAULT_BPM).
- // Recommendation: create a static global tempo map object. When
- // any operation that needs a tempo map gets NULL, use the global
- // tempo map. (Exception: any operation that would modify the
- // tempo map should raise an error -- you don't want to change the
- // default tempo map.)
- virtual void set_time_map(Alg_time_map *map);
- Alg_time_map *get_time_map() { return time_map; }
-
- // Methods to create events. The returned event is owned by the caller.
- // Use delete to get rid of it unless you call add() -- see below.
- //
- Alg_note *create_note(double time, int channel, int identifier,
- float pitch, float loudness, double duration);
- // Note: after create_update(), caller should use set_*_value() to
- // initialize the attribute/value pair:
- Alg_update *create_update(double time, int channel, int identifier);
- // Adds a new event - it is automatically inserted into the
- // correct order in the sequence based on its timestamp.
- // The ownership passes from the caller to this Alg_seq. The
- // event is not copied.
- virtual void add(Alg_event *event) { insert(event); }
-
- //
- // Editing regions
- //
-
- // Deletes the notes that start within the given region
- // and returns them in a new sequence. The start times
- // of the notes in the returned sequence are shifted
- // by -t. The notes after the region get shifted over
- // to fill the gap. In an Alg_seq, the tempo track is edited
- // in a similar way
- // and the cut tempo information is retained in the new seq.
- // ONLY NOTES THAT START WITHIN THE REGION ARE CUT unless
- // "all" is true in which case all notes that intersect
- // the region are copied. CUT NOTES
- // MAY EXTEND BEYOND THE DURATION OF THE RESULTING SEQ.
- // The return type is the same as this (may be Alg_seq).
- // All times including len are interpreted according to
- // units_are_seconds in the track.
- virtual Alg_track *cut(double t, double len, bool all);
-
- // Like cut() but doesn't remove the notes from the original
- // sequence. The Alg_events are copied, not shared. ONLY EVENTS
- // THAT START WITHIN THE REGION ARE COPIED unless "all" is true
- // in which case all notes that intersect the region are
- // copied. COPIED NOTES MAY
- // EXTEND BEYOND THE DURATION OF THE RESULTING SEQ.
- // The return type is the same as this (may be Alg_seq).
- virtual Alg_track *copy(double t, double len, bool all);
-
- // Inserts a sequence in the middle, shifting some notes
- // over by the duration of the seq, which is first converted
- // to the same units (seconds or beats) as this. (This makes
- // a differece because the pasted data may change the tempo,
- // and notes that overlap the borders will then experience
- // a tempo change.)
- // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE
- // COPIED, NOT SHARED.
- // The type of seq must be Alg_seq if seq is an Alg_seq, or
- // Alg_track if seq is an Alg_track or an Alg_event_list.
- virtual void paste(double t, Alg_event_list *seq); // Shifts notes
-
- // Merges two sequences with a certain offset. The offset is
- // interpreted as either beats or seconds according to the
- // current units of this, and seq is converted to the same
- // units as this. Except for a possible conversion to beats
- // or seconds, the tempo track of seq (if any) is ignored.
- // (There is no way to merge tempo tracks.)
- // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE
- // COPIED, NOT SHARED.
- // The type of seq must be Alg_seq if seq is an Alg_seq, or
- // Alg_track if seq is an Alg_track or an Alg_event_list.
- virtual void merge(double t, Alg_event_list_ptr seq);
-
- // Deletes and shifts notes to fill the gap. The tempo track
- // is also modified accordingly. ONLY EVENTS THAT START WITHIN
- // THE REGION ARE DELETED unless "all" is true, in which case
- // all notes that intersect the region are cleared.
- // NOTES THAT EXTEND FROM BEFORE THE
- // REGION INTO THE REGION RETAIN THEIR DURATION IN EITHER
- // BEATS OR SECONDS ACCORDING TO THE CURRENT UNITS OF this.
- virtual void clear(double t, double len, bool all);
-
- // Deletes notes but doesn't shift. If the "all" argument
- // is true, deletes all notes that intersect the range at all,
- // not just those that start within it. The tempo track is
- // not affected.
- virtual void silence(double t, double len, bool all);
-
- // Simply shifts notes past time t over by len, which is given
- // in either beats or seconds according to the units of this.
- // The resulting interveal (t, t+len) may in fact contain notes
- // that begin before t. The durations of notes are not changed.
- // If this is an Alg_seq, the tempo track is expanded at t also.
- virtual void insert_silence(double t, double len);
-
- //
- // Accessing for screen display
- //
-
- // A useful generic function to retrieve only certain
- // types of events. The masks should be bit-masks defined
- // somewhere else. Part of the mask allows us to search for
- // selected events. If this is an Alg_seq, search all tracks
- // (otherwise, call track[i].find())
- // If channel_mask == 0, accept ALL channels
- virtual Alg_event_list *find(double t, double len, bool all,
- long channel_mask, long event_type_mask);
-
- //
- // MIDI playback
- //
- // See Alg_iterator
-} *Alg_track_ptr, &Alg_track_ref;
-
-
-// Alg_time_sig represents a single time signature;
-// although not recommended, time_signatures may have arbitrary
-// floating point values, e.g. 4.5 beats per measure
-typedef class Alg_time_sig {
-public:
- double beat; // when does this take effect?
- double num; // what is the "numerator" (top number?)
- double den; // what is the "denominator" (bottom number?)
- Alg_time_sig(double b, double n, double d) {
- beat = b; num = n; den = d;
- }
- Alg_time_sig() {
- beat = 0; num = 0; den = 0;
- }
- void beat_to_measure(double beat, double *measure, double *m_beat,
- double *num, double *den);
-
-} *Alg_time_sig_ptr;
-
-
-// Alg_time_sigs is a dynamic array of time signatures
-//
-// The default (empty) time_sigs has 4/4 time at beat 0.
-// Each time_sig object in time_sigs represents the beginning
-// of a measure. If there is a beat missing, e.g. in the first
-// measure, you can represent this by inserting another
-// time_sig at the next measure beginning. Each time_sig implies
-// an infinite sequence of full measures until the next time_sig.
-// If you insert a time_sig and one already exist near the same
-// beat, the old one is replaced, thus re-barring every measure
-// until the next time_sig.
-class Alg_time_sigs {
-private:
- long maxlen;
- void expand(); // make more space
- long len;
- Alg_time_sig_ptr time_sigs;
-public:
- Alg_time_sigs() {
- maxlen = len = 0;
- time_sigs = NULL;
- }
- Alg_time_sig &operator[](int i) { // fetch a time signature
- assert(i >= 0 && i < len);
- return time_sigs[i];
- }
- ~Alg_time_sigs() {
- if (time_sigs) delete[] 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
- 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
-};
-
-
-// a sequence of Alg_events objects
-typedef class Alg_tracks {
-private:
- long maxlen;
- void expand();
- void expand_to(int new_max);
- long len;
-public:
- Alg_track_ptr *tracks; // tracks is array of pointers
- Alg_track &operator[](int i) {
- assert(i >= 0 && i < len);
- return *tracks[i];
- }
- long length() { return len; }
- Alg_tracks() {
- maxlen = len = 0;
- tracks = NULL;
- }
- ~Alg_tracks();
- // Append a track to tracks. This Alg_tracks becomes the owner of track.
- void append(Alg_track_ptr track);
- void add_track(int track_num, Alg_time_map_ptr time_map, bool seconds);
- void reset();
-} *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;
-
-
-// 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
- void serialize_seq();
- Alg_error error; // error code set by file readers
- // an internal function used for writing Allegro track names
- Alg_event_ptr write_track_name(std::ostream &file, int n,
- Alg_events &events);
-public:
- int channel_offset_per_track; // used to encode track_num into channel
- Alg_tracks track_list; // array of Alg_events
- Alg_time_sigs time_sig;
- int beat_x;
- void basic_initialization() {
- error = alg_no_error;
- units_are_seconds = true; type = 's';
- channel_offset_per_track = 0;
- add_track(0); // default is one empty track
- }
- Alg_seq() {
- basic_initialization();
- }
- // copy constructor -- if track is an Alg_seq, make a copy; if
- // track is just an Alg_track, the track becomes track 0
- 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();
- int get_read_error() { return error; }
- void serialize(void **buffer, long *bytes);
- void copy_time_sigs_to(Alg_seq *dest); // a utility function
- void set_time_map(Alg_time_map *map);
-
- // encode sequence structure into contiguous, moveable memory block
- // address of newly allocated memory is assigned to *buffer, which must
- // be freed by caller; the length of data is assigned to *len
- void unserialize_seq();
-
- // write an ascii representation to file
- void write(std::ostream &file, bool in_secs);
- // returns true on success
- bool write(const char *filename);
- void smf_write(std::ofstream &file);
- bool smf_write(const char *filename);
-
- // Returns the number of tracks
- int tracks();
-
- // create a track
- void add_track(int track_num) {
- track_list.add_track(track_num, get_time_map(), units_are_seconds);
- }
-
- // Return a particular track. This Alg_seq owns the track, so the
- // caller must not delete the result.
- Alg_track_ptr track(int);
-
- virtual Alg_event_ptr &operator[](int i);
-
- virtual void convert_to_seconds();
- virtual void convert_to_beats();
-
- Alg_track_ptr cut_from_track(int track_num, double start, double dur,
- bool all);
- Alg_seq *cut(double t, double len, bool all);
- void insert_silence_in_track(int track_num, double t, double len);
- void insert_silence(double t, double len);
- Alg_track_ptr copy_track(int track_num, double t, double len, bool all);
- Alg_seq *copy(double start, double len, bool all);
- void paste(double start, Alg_seq *seq);
- virtual void clear(double t, double len, bool all);
- virtual void merge(double t, Alg_event_list_ptr seq);
- virtual void silence(double t, double len, bool all);
- void clear_track(int track_num, double start, double len, bool all);
- void silence_track(int track_num, double start, double len, bool all);
- Alg_event_list_ptr find_in_track(int track_num, double t, double len,
- bool all, long channel_mask,
- long event_type_mask);
-
- // find index of first score event after time
- long seek_time(double time, int track_num);
- bool insert_beat(double time, double beat);
- // warning: insert_tempo may change representation from seconds to beats
- bool insert_tempo(double bpm, double beat);
-
- // 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
- bool set_tempo(double bpm, double start_beat, double end_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
-} *Alg_seq_ptr, &Alg_seq_ref;
-
-
-// see Alg_seq::Alg_seq() constructors that read from files
-// the following are for internal library implementation and are
-// moved to *_internal.h header files.
-//Alg_seq_ptr alg_read(std::istream &file, Alg_seq_ptr new_seq);
-//Alg_seq_ptr alg_smf_read(std::istream &file, Alg_seq_ptr new_seq);
-#endif
diff --git a/plugins/MidiImport/portsmf/allegrord.cpp b/plugins/MidiImport/portsmf/allegrord.cpp
deleted file mode 100644
index 7a1f5beed6f..00000000000
--- a/plugins/MidiImport/portsmf/allegrord.cpp
+++ /dev/null
@@ -1,753 +0,0 @@
-#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
-*/
diff --git a/plugins/MidiImport/portsmf/allegroserial.cpp b/plugins/MidiImport/portsmf/allegroserial.cpp
deleted file mode 100644
index e6b52f339f1..00000000000
--- a/plugins/MidiImport/portsmf/allegroserial.cpp
+++ /dev/null
@@ -1,2 +0,0 @@
-// allegroserial.cpp -- convert track to memory buffer and back to structure
-
diff --git a/plugins/MidiImport/portsmf/allegrosmfrd.cpp b/plugins/MidiImport/portsmf/allegrosmfrd.cpp
deleted file mode 100644
index 49e2ef03ee6..00000000000
--- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
-// 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);
-}
diff --git a/plugins/MidiImport/portsmf/allegrosmfwr.cpp b/plugins/MidiImport/portsmf/allegrosmfwr.cpp
deleted file mode 100644
index 5a76c44ed9f..00000000000
--- a/plugins/MidiImport/portsmf/allegrosmfwr.cpp
+++ /dev/null
@@ -1,649 +0,0 @@
-// allegrosmfwr.cpp -- Allegro Standard Midi File Write
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-using namespace std;
-#include "allegro.h"
-
-// event_queue is a list element that keeps track of pending
-// things to write to a track, including note-ons, note-offs,
-// updates, tempo changes, and time signatures
-//
-class event_queue{
-public:
- char type;//'n' for note, 'o' for off, 's' for time signature,
- // 'c' for tempo changes
- double time;
- long index; //of the event in mSeq->notes
- class event_queue *next;
- event_queue(char t, double when, long x, class event_queue *n) {
- type = t; time = when; index = x; next = n; }
-};
-
-
-class Alg_smf_write {
-public:
- Alg_smf_write(Alg_seq_ptr seq);
- ~Alg_smf_write();
- long channels_per_track; // used to encode track number into chan field
- // 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 */);
-
-private:
- long previous_divs; // time in ticks of most recently written event
-
- void write_track(int i);
- void write_tempo(int divs, int tempo);
- void write_tempo_change(int i);
- void write_time_signature(int i);
- 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_midi_channel_prefix(Alg_update_ptr update);
- void write_smpteoffset(Alg_update_ptr update, char *s);
- void write_data(int data);
- int to_midi_channel(int channel);
- int to_track(int channel);
-
- ostream *out_file;
-
- Alg_seq_ptr seq;
-
- int num_tracks; // number of tracks not counting tempo track
- int division; // divisions per quarter note, default = 120
- int initial_tempo;
-
- int timesig_num; // numerator of time signature
- int timesig_den; // denominator of time signature
- double timesig_when; // time of time signature
-
- int keysig; // number of sharps (+) or flats (-), -99 for undefined
- char keysig_mode; // 'M' or 'm' for major/minor
- double keysig_when; // time of key signature
-
- void write_delta(double event_time);
- void write_varinum(int num);
- void write_16bit(int num);
- void write_24bit(int num);
- void write_32bit(int num);
-};
-
-#define ROUND(x) (int) ((x)+0.5)
-
-Alg_smf_write::Alg_smf_write(Alg_seq_ptr a_seq)
-{
- out_file = NULL;
-
- // at 100bpm (a nominal tempo value), we would like a division
- // to represent 1ms of time. So
- // d ticks/beat * 100 beats/min = 60,000 ms/min * 1 tick/ms
- // solving for d, d = 600
- division = 600; // divisions per quarter note
- timesig_num = timesig_den = 0; // initially undefined
- keysig = -99;
- keysig_mode = 0;
- initial_tempo = 500000;
-
- seq = a_seq;
-
- previous_divs = 0; // used to compute deltas for midifile
-}
-
-
-Alg_smf_write::~Alg_smf_write()
-{
-}
-
-
-// sorting is quite subtle due to rounding
-// For example, suppose times from a MIDI file are exact, but in
-// decimal round to TW0.4167 Q0.3333. Since the time in whole notes
-// rounded up, this note will start late. Even though the duration
-// rounded down, the amount is 1/4 as much because units are quarter
-// notes. Therefore, the total roundup is 0.0001 beats. This is
-// enough to cause the note to sort later in the queue, perhaps
-// coming after a new note-on on the same pitch, and resulting in
-// a turning on-off, on-off into on, on, off, off if data is moved
-// to Allegro (ascii) format with rounding and then back to SMF.
-//
-// The solution here is to consider things that round to the same
-// tick to be simultaneous. Then, be sure to deal with note-offs
-// before note-ons. We're going to do that by using event_queue
-// times that are rounded to the nearest tick time. Except note-offs
-// are going to go in with times that are 1/4 tick earlier so they
-// get scheduled first, but still end up on the same tick.
-//
-event_queue* push(event_queue *queue, event_queue *event)
-{
- // printf("push: %.6g, %c, %d\n", event->time, event->type, event->index);
- if (queue == NULL) {
- event->next = NULL;
- return event;
- }
-
- event_queue *marker1 = NULL;
- event_queue *marker2 = queue;
- while (marker2 != NULL && marker2->time <= event->time) {
- marker1 = marker2;
- marker2 = marker2->next;
- }
- event->next = marker2;
- if (marker1 != NULL) {
- marker1->next=event;
- return queue;
- } else return event;
-}
-
-
-void print_queue(event_queue *q)
-{
- printf("Printing queue. . .\n");
- event_queue *q2=q;
- while (q2) {
- printf("%c at %f ;", q2->type, q2->time);
- q2 = q2->next;
- }
- printf("\nDone printing.\n");
-}
-
-
-void Alg_smf_write::write_note(Alg_note_ptr note, bool on)
-{
- double event_time = (on ? note->time : note->time + note->dur);
- write_delta(event_time);
-
- //printf("deltaDivisions: %d, beats elapsed: %g, on? %c\n", deltaDivisions, note->time, on);
-
- char chan = (note->chan & 15);
- int pitch = int(note->pitch + 0.5);
- if (pitch < 0) {
- pitch = pitch % 12;
- } else if (pitch > 127) {
- pitch = (pitch % 12) + 120; // put pitch in 10th octave
- if (pitch > 127) pitch -= 12; // or 9th octave
- }
- out_file->put(0x90 + chan);
- out_file->put(pitch);
- if (on) {
- int vel = (int) note->loud;
- if (vel <= 0) vel = 1;
- write_data(vel);
- } else out_file->put(0); // note-off indicated by velocty zero
-}
-
-
-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(1); // length
- out_file->put(to_midi_channel(update->chan));
- // one thing odd about the Std MIDI File spec is that once
- // you turn on MIDI Channel Prefix, there seems to be no
- // way to cancel it unless a non-Meta event shows up. We
- // don't do any analysis to avoid assigning channels to
- // meta events.
- }
-}
-
-
-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(type);
- out_file->put((char) strlen(update->parameter.s));
- *out_file << update->parameter.s;
-}
-
-
-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(5); // length
- for (int i = 0; i < 5; i++) *out_file << s[i];
-}
-
-
-// write_data - limit data to the range of [0...127] and write it
-void Alg_smf_write::write_data(int data)
-{
- if (data < 0) data = 0;
- else if (data > 0x7F) data = 0x7F;
-
- out_file->put(data);
-}
-
-
-int Alg_smf_write::to_midi_channel(int channel)
-{
- // allegro track number is stored as multiple of 100
- // also mask off all but 4 channel bits just in case
- if (channels_per_track > 0) channel %= channels_per_track;
- return channel & 0xF;
-}
-
-
-int Alg_smf_write::to_track(int channel)
-{
- if (channel == -1) return 0;
- return channel / channels_per_track;
-}
-
-
-static char hex_to_nibble(char c)
-{
- if (isalpha(c)) {
- return 10 + (toupper(c) - 'A');
- } else {
- return c - '0';
- }
-}
-
-
-static char hex_to_char(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)
-{
- int len = strlen(msg) / 2;
- out_file->put(type_byte);
- write_varinum(len);
- for (int i = 0; i < len; i++) {
- out_file->put(hex_to_char(msg));
- msg += 2;
- }
-}
-
-
-void Alg_smf_write::write_update(Alg_update_ptr update)
-{
- char *name = update->parameter.attr_name();
-
- /****Non-Meta Events****/
- if (!strcmp(name, "pressurer")) {
- write_delta(update->time);
- if (update->get_identifier() < 0) { // channel pressure message
- out_file->put(0xD0 + to_midi_channel(update->chan));
- write_data((int)(update->parameter.r * 127));
- } else { // just 1 key -- poly pressure
- out_file->put(0xA0 + to_midi_channel(update->chan));
- write_data(update->get_identifier());
- write_data((int)(update->parameter.r * 127));
- }
- } else if (!strcmp(name, "programi")) {
- write_delta(update->time);
- out_file->put(0xC0 + to_midi_channel(update->chan));
- write_data(update->parameter.i);
- } else if (!strcmp(name, "bendr")) {
- int temp = ROUND(0x2000 * (update->parameter.r + 1));
- if (temp > 0x3fff) temp = 0x3fff; // 14 bits maximum
- if (temp < 0) temp = 0;
- int c1 = temp & 0x7F; // low 7 bits
- int c2 = temp >> 7; // high 7 bits
- write_delta(update->time);
- out_file->put(0xE0 + to_midi_channel(update->chan));
- write_data(c1);
- write_data(c2);
- } else if (!strncmp(name, "control", 7) &&
- update->parameter.attr_type() == 'r') {
- int ctrlnum = atoi(name + 7);
- int val = ROUND(update->parameter.r * 127);
- write_delta(update->time);
- out_file->put(0xB0 + to_midi_channel(update->chan));
- write_data(ctrlnum);
- write_data(val);
- } else if (!strcmp(name, "sysexs") &&
- update->parameter.attr_type() == 's') {
- 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
- }
- write_delta(update->time);
- write_binary(0xF0, s);
- } else if (!strcmp(name, "sqspecifics") &&
- update->parameter.attr_type() == 's') {
- char *s = update->parameter.s;
- write_delta(update->time);
- out_file->put(0xFF);
- write_binary(0x7F, s);
-
- /****Text Events****/
- } else if (!strcmp(name, "texts")) {
- write_text(update, 0x01);
- } else if (!strcmp(name, "copyrights")) {
- write_text(update, 0x02);
- } else if (!strcmp(name, "seqnames") || !strcmp(name, "tracknames")) {
- write_text(update, 0x03);
- } else if (!strcmp(name, "instruments")) {
- write_text(update, 0x04);
- } else if (!strcmp(name, "lyrics")) {
- write_text(update, 0x05);
- } else if (!strcmp(name, "markers")) {
- write_text(update, 0x06);
- } else if (!strcmp(name, "cues")) {
- write_text(update, 0x07);
- } else if (!strcmp(name, "miscs")) {
- write_text(update, 0x08);
-
- /****Other Events****/
- } else if (!strcmp(name, "smpteoffsets")) {
-#define decimal(p) (((p)[0] - '0') * 10 + ((p)[1] - '0'))
- // 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;
- int len = strlen(s);
- char smpteoffset[5];
- if (len < 24) return; // not long enough, must be bad format
- int fps = 0;
- if (s[0] == '2') {
- if (s[1] == '4') fps = 0;
- else if (s[1] == '5') fps = 1;
- else if (s[1] == '9') {
- fps = 2;
- if (len != 27) return; // not right length
- s += 3; // cancel effect of longer string
- }
- } else fps = 3;
- s += 6; int hours = decimal(s);
- s += 4; int mins = decimal(s);
- s += 4; int secs = decimal(s);
- s += 4; int frames = decimal(s);
- s += 3; int subframes = decimal(s);
- smpteoffset[0] = (fps << 6) + hours;
- smpteoffset[1] = mins;
- smpteoffset[2] = secs;
- smpteoffset[3] = frames;
- smpteoffset[4] = subframes;
- write_smpteoffset(update, smpteoffset);
-
- // key signature is special because it takes two events in the Alg_seq
- // structure to make one midi file event. When we encounter one or
- // the other event, we'll just record it in the Alg_smf_write object.
- // After both events are seen, we write the data. (See below.)
- } else if (!strcmp(name, "keysigi")) {
- keysig = update->parameter.i;
- keysig_when = update->time;
- } else if (!strcmp(name, "modea")) {
- if (!strcmp(alg_attr_name(update->parameter.a), "major"))
- keysig_mode = 'M';
- else keysig_mode = 'm';
- keysig_when = update->time;
- }
- 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(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)
- out_file->put(keysig & 0xFF);
- out_file->put(keysig_mode == 'm');
- keysig = -99;
- keysig_mode = false;
- }
- //printf("Update: %s, key: %g\n", update->parameter.attr_name(), update->key);
-}
-
-
-// see notes on event_queue::push, TICK_TIME converts from beat to
-// the number of the nearest tick. The second parameter is an offset in
-// quarter ticks. By scheduling with -1, note-offs should get dispatched
-// first. Note that TICK_TIME only determines the order of events, so
-// it is ok to change units from beats to ticks, saving a divide.
-#define TICK_TIME(t, o) (ROUND((t) * division) + 0.25 * (o))
-
-void Alg_smf_write::write_track(int i)
-{
- int j = 0; // note index
- Alg_events ¬es = seq->track_list[i];
- event_queue *pending = NULL;
- if (notes.length() > 0) {
- pending = new event_queue('n', TICK_TIME(notes[j]->time, 0), 0, NULL);
- }
- if (i == 0) { // track 0 may have tempo and timesig info
- if (seq->get_time_map()->last_tempo_flag || seq->get_time_map()->beats.len > 0) {
- pending = push(pending, new event_queue('c', 0.0, 0, NULL));
- }
- if (seq->time_sig.length() > 0) {
- pending = push(pending, new event_queue('s',
- TICK_TIME(seq->time_sig[0].beat, 0), 0, NULL));
- }
- }
- while (pending) {
- event_queue *current = pending;
- pending = pending->next;
- if (current->type == 'n') {
- Alg_note_ptr n = (Alg_note_ptr) notes[current->index];
- if (n->is_note()) {
- write_note(n, true);
- pending = push(pending, new event_queue('o',
- TICK_TIME(n->time + n->dur, -1), current->index, NULL));
- } else if (n->is_update()) {
- Alg_update_ptr u = (Alg_update_ptr) n;
- write_update(u);
- }
- int next = current->index + 1;
- if (next < notes.length()) {
- current->time = TICK_TIME(notes[next]->time, 0);
- current->index = next;
- pending = push(pending, current);
- }
- } else if (current->type == 'o') { //note-off
- Alg_note_ptr n = (Alg_note_ptr) notes[current->index];
- write_note(n, false);
- delete current;
- } else if (current->type == 'c') { // tempo change
- write_tempo_change(current->index);
- current->index++; // -R
- if (current->index < seq->get_time_map()->beats.len) {
- current->time =
- TICK_TIME(seq->get_time_map()->
- beats[current->index].beat, 0);
- pending = push(pending, current);
- } else {
- delete current;
- }
- } else if (current->type == 's') { // time sig
- write_time_signature(current->index);
- current->index++;
- if (current->index < seq->time_sig.length()) {
- current->time =
- TICK_TIME(seq->time_sig[current->index].beat, 0);
- pending = push(pending, current);
- } else {
- delete current;
- }
- }
- }
-}
-
-
-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);
- write_24bit((int)tempo);
-}
-
-
-void Alg_smf_write::write_tempo_change(int i)
- // i is index of tempo map
-{
- // extract tempo map
- Alg_beats &b = seq->get_time_map()->beats;
- double tempo;
- long divs;
- if (i < seq->get_time_map()->beats.len - 1) {
- tempo = 1000000 * ((b[i+1].time - b[i].time) /
- (b[i+1].beat - b[i].beat));
- divs = ROUND(b[i].beat * division);
- write_tempo(divs, ROUND(tempo));
- } else if (seq->get_time_map()->last_tempo_flag) { // write the final tempo
- divs = ROUND(division * b[i].beat);
- tempo = (1000000.0 / seq->get_time_map()->last_tempo);
- write_tempo(divs, ROUND(tempo));
- }
-}
-
-
-void Alg_smf_write::write_time_signature(int i)
-{
- Alg_time_sigs &ts = seq->time_sig;
- // 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(ROUND(ts[i].num));
- int den = ROUND(ts[i].den);
- int den_byte = 0;
- while (den > 1) { // compute the log2 of denominator
- den_byte++;
- den >>= 1;
- }
- out_file->put(den_byte);
- out_file->put(24); // clocks per quarter
- out_file->put(8); // 32nd notes per 24 clocks
-}
-
-
-
-void Alg_smf_write::write(ofstream &file)
-{
- int track_len_offset;
- int track_end_offset;
- int track_len;
-
- out_file = &file;
-
- // Header
- file << "MThd";
-
- write_32bit(6); // chunk length
-
- write_16bit(1); // format 1 MIDI file
-
- write_16bit(seq->tracks()); // number of tracks
- write_16bit(division); // divisions per quarter note
-
-
- // write_ all tracks
- seq->convert_to_beats();
- int i;
- for (i = 0; i < seq->tracks(); i++) {
- previous_divs = 0;
- *out_file << "MTrk";
- track_len_offset = out_file->tellp();
- write_32bit(0); // track len placeholder
-
- write_track(i);
-
- // End of track event
- write_varinum(0); // delta time
- out_file->put(0xFF);
- out_file->put(0x2F);
- out_file->put(0x00);
-
- // Go back and write in the length of the track
- track_end_offset = out_file->tellp();
- track_len = track_end_offset - track_len_offset - 4;
- out_file->seekp(track_len_offset);
- write_32bit(track_len);
- out_file->seekp(track_end_offset);
- }
-}
-
-
-void Alg_smf_write::write_16bit(int num)
-{
- out_file->put((num & 0xFF00) >> 8);
- out_file->put(num & 0xFF);
-}
-
-void Alg_smf_write::write_24bit(int num)
-{
- out_file->put((num & 0xFF0000) >> 16);
- out_file->put((num & 0xFF00) >> 8);
- out_file->put((num & 0xFF));
-}
-
-void Alg_smf_write::write_32bit(int num)
-{
- out_file->put((num & 0xFF000000) >> 24);
- out_file->put((num & 0xFF0000) >> 16);
- out_file->put((num & 0xFF00) >> 8);
- out_file->put((num & 0xFF));
-}
-
-
-void Alg_smf_write::write_delta(double event_time)
-{
- // divisions is ideal absolute time in divisions
- long divisions = ROUND(division * event_time);
- long delta_divs = divisions - previous_divs;
- write_varinum(delta_divs);
- previous_divs = divisions;
-}
-
-
-void Alg_smf_write::write_varinum(int value)
-{
- if(value<0) value=0;//this line should not have to be here!
- int buffer;
-
- buffer = value & 0x7f;
- while ((value >>= 7) > 0) {
- buffer <<= 8;
- buffer |= 0x80;
- buffer += (value & 0x7f);
- }
-
- for(;;) {
- out_file->put(buffer);
- if (buffer & 0x80)
- buffer >>= 8;
- else
- break;
- }
-}
-
-
-void Alg_seq::smf_write(ofstream &file)
-{
- Alg_smf_write writer(this);
- writer.write(file);
-}
-
-bool Alg_seq::smf_write(const char *filename)
-{
- ofstream outf(filename, ios::binary | ios::out);
- if (outf.fail()) return false;
- smf_write(outf);
- outf.close();
- return true;
-}
-
diff --git a/plugins/MidiImport/portsmf/allegrowr.cpp b/plugins/MidiImport/portsmf/allegrowr.cpp
deleted file mode 100644
index 3b266f84cac..00000000000
--- a/plugins/MidiImport/portsmf/allegrowr.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-// allegrowr.cpp -- write sequence to an Allegro file (text)
-
-#include "debug.h"
-#include "stdlib.h"
-#include
-#include
-#include
-#include
-#include
-#include "memory.h"
-using namespace std;
-#include "strparse.h"
-#include "allegro.h"
-
-// Note about precision: %g prints 6 significant digits. For 1ms precision,
-// the maximum magnitude is 999.999, i.e. 1000s < 17minutes. For anything
-// over 1000s, time in seconds will be printed with 10ms precision, which
-// is not good. Therefore, times and durations are printed as %.4d, which
-// gives 100us precision.
-// The following define allows you to change this decision:
-/* #define TIMFMT "%.4d" */
-#define TIMPREC 4
-#define TIMFMT fixed << setprecision(TIMPREC)
-#define GFMT resetiosflags(ios::floatfield) << setprecision(6)
-
-void parameter_print(ostream &file, Alg_parameter_ptr p)
-{
- file << " -" << p->attr_name() << ":";
- switch (p->attr_type()) {
- case 'a':
- file << "'" << alg_attr_name(p->a) << "'";
- break;
- case 'i':
- file << p->i;
- break;
- case 'l':
- file << (p->l ? "true" : "false");
- break;
- case 'r':
- file << p->r;
- break;
- case 's': {
- string str;
- string_escape(str, p->s, "\"");
- file << str;
- break;
- }
- }
-}
-
-Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n,
- Alg_events &events)
-// write #track
-// if we write the name on the "#track" line, then we do *not* want
-// to write again as an update: "-seqnames:"Jordu", so if we do
-// 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;
- 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;
- if (u->parameter.attr == attr) {
- file << " " << u->parameter.s;
- break;
- }
- }
- }
- file << endl;
- return e;
-}
-
-
-void Alg_seq::write(ostream &file, bool in_secs)
-{
- int i, j;
- if (in_secs) convert_to_seconds();
- else convert_to_beats();
- 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++) {
- Alg_beat_ptr b = &(beats[i]);
- if (in_secs) {
- file << "T" << TIMFMT << b->time;
- } else {
- file << "TW" << TIMFMT << b->beat / 4;
- }
- double tempo = (beats[i + 1].beat - b->beat) /
- (beats[i + 1].time - beats[i].time);
- file << " -tempor:" << GFMT << tempo * 60 << "\n";
- }
- if (time_map->last_tempo_flag) { // we have final tempo:
- Alg_beat_ptr b = &(beats[beats.len - 1]);
- if (in_secs) {
- file << "T" << TIMFMT << b->time;
- } else {
- file << "TW" << TIMFMT << b->beat / 4;
- }
- file << " -tempor:" << GFMT << time_map->last_tempo * 60.0 << "\n";
- }
-
- // write the time signatures
- for (i = 0; i < time_sig.length(); i++) {
- Alg_time_sig &ts = time_sig[i];
- double time = ts.beat;
- if (in_secs) {
- file << "T" << TIMFMT << time << " V- -timesig_numr:" <<
- GFMT << ts.num << "\n";
- file << "T" << TIMFMT << time << " V- -timesig_denr:" <<
- GFMT << ts.den << "\n";
- } else {
- double wholes = ts.beat / 4;
- file << "TW" << TIMFMT << wholes << " V- -timesig_numr:" <<
- GFMT << ts.num << "\n";
- file << "TW" << TIMFMT << wholes << " V- -timesig_denr:" <<
- GFMT << ts.den << "\n";
- }
- }
-
- for (j = 0; j < track_list.length(); j++) {
- Alg_events ¬es = track_list[j];
- if (j != 0) update_to_skip = write_track_name(file, j, notes);
- // now write the notes at beat positions
- for (i = 0; i < notes.length(); i++) {
- Alg_event_ptr e = notes[i];
- // if we already wrote this event as a track or sequence name,
- // do not write it again
- if (e == update_to_skip) continue;
- double start = e->time;
- if (in_secs) {
- file << "T" << TIMFMT << start;
- } else {
- file << "TW" << TIMFMT << start / 4;
- }
- // write the channel as Vn or V-
- if (e->chan == -1) file << " V-";
- else file << " V" << e->chan;
- // write the note or update data
- if (e->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) e;
- double dur = n->dur;
- file << " K" << n->get_identifier() <<
- " P" << GFMT << n->pitch;
- if (in_secs) {
- file << " U" << TIMFMT << dur;
- } else {
- file << " Q" << TIMFMT << dur;
- }
- file << " L" << GFMT << n->loud;
- Alg_parameters_ptr p = n->parameters;
- while (p) {
- parameter_print(file, &(p->parm));
- p = p->next;
- }
- } else { // an update
- assert(e->is_update());
- Alg_update_ptr u = (Alg_update_ptr) e;
- if (u->get_identifier() != -1) {
- file << " K" << u->get_identifier();
- }
- parameter_print(file, &(u->parameter));
- }
- file << "\n";
- }
- }
-}
-
-bool Alg_seq::write(const char *filename)
-{
- ofstream file(filename);
- if (file.fail()) return false;
- write(file, units_are_seconds);
- file.close();
- return true;
-}
diff --git a/plugins/MidiImport/portsmf/license.txt b/plugins/MidiImport/portsmf/license.txt
deleted file mode 100644
index 6f86ce0d4fa..00000000000
--- a/plugins/MidiImport/portsmf/license.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Portsmf: Portable Standard MIDI File Library
- *
- * license.txt -- a copy of the Portsmf copyright notice and license information
- *
- * Latest version available at: http://sourceforge.net/projects/portmedia
- *
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- * Copyright (c) 2001-2006 Roger B. Dannenberg
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/*
- * The text above constitutes the entire Portsmf license; however,
- * the PortMusic community also makes the following non-binding requests:
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version. It is also
- * requested that these non-binding requests be included along with the
- * license above.
- */
diff --git a/plugins/MidiImport/portsmf/mfmidi.cpp b/plugins/MidiImport/portsmf/mfmidi.cpp
deleted file mode 100644
index 52f93b83764..00000000000
--- a/plugins/MidiImport/portsmf/mfmidi.cpp
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Read a Standard MIDI File. Externally-assigned function pointers are
- * called upon recognizing things in the file. See midifile(3).
- */
-
-/*****************************************************************************
-* Change Log
-* Date | who : Change
-*-----------+-----------------------------------------------------------------
-* 2-Mar-92 | GWL : created changelog; MIDIFILE_ERROR to satisfy compiler
-*****************************************************************************/
-
-#include "stdio.h"
-#include "mfmidi.h"
-#include "string.h"
-
-#define MIDIFILE_ERROR -1
-
-/* public stuff */
-extern int abort_flag;
-
-
-void Midifile_reader::midifile()
-{
- int ntrks;
- midifile_error = 0;
-
- ntrks = readheader();
- if (midifile_error) return;
- if (ntrks <= 0) {
- mferror("No tracks!");
- /* no need to return since midifile_error is set */
- }
- while (ntrks-- > 0 && !midifile_error) readtrack();
-}
-
-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. */
-{
- int nread = 0;
- char b[4];
- char buff[32];
- int c;
- const char *errmsg = "expecting ";
-
- retry:
- while ( nread<4 ) {
- c = Mf_getc();
- if ( c == EOF ) {
- errmsg = "EOF while expecting ";
- goto err;
- }
- b[nread++] = c;
- }
- /* See if we found the 4 characters we're looking for */
- if ( s[0]==b[0] && s[1]==b[1] && s[2]==b[2] && s[3]==b[3] )
- return(0);
- if ( skip ) {
- /* If we are supposed to skip initial garbage, */
- /* try again with the next character. */
- b[0]=b[1];
- b[1]=b[2];
- b[2]=b[3];
- nread = 3;
- goto retry;
- }
- err:
- (void) strcpy(buff,errmsg);
- (void) strcat(buff,s);
- mferror(buff);
- return(0);
-}
-
-int Midifile_reader::egetc()
- /* read a single character and abort on EOF */
-{
- int c = Mf_getc();
-
- if ( c == EOF ) {
- mferror("premature EOF");
- return EOF;
- }
- Mf_toberead--;
- return(c);
-}
-
-int Midifile_reader::readheader()
- /* read a header chunk */
-{
- int format, ntrks, division;
-
- if ( readmt("MThd",Mf_skipinit) == EOF )
- return(0);
-
- Mf_toberead = read32bit();
- if (midifile_error) return MIDIFILE_ERROR;
- format = read16bit();
- if (midifile_error) return MIDIFILE_ERROR;
- ntrks = read16bit();
- if (midifile_error) return MIDIFILE_ERROR;
- division = read16bit();
- if (midifile_error) return MIDIFILE_ERROR;
-
- Mf_header(format,ntrks,division);
-
- /* flush any extra stuff, in case the length of header is not 6 */
- while ( Mf_toberead > 0 && !midifile_error)
- (void) egetc();
- return(ntrks);
-}
-
-void Midifile_reader::readtrack()
- /* read a track chunk */
-{
- /* This array is indexed by the high half of a status byte. It's */
- /* value is either the number of bytes needed (1 or 2) for a channel */
- /* message, or 0 (meaning it's not a channel message). */
- static int chantype[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
- 2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
- };
- long lookfor, lng;
- int c, c1, type;
- int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
- int running = 0; /* 1 when running status used */
- int status = 0; /* (possibly running) status byte */
- int needed;
-
- if ( readmt("MTrk",0) == EOF )
- return;
-
- Mf_toberead = read32bit();
-
- if (midifile_error) return;
-
- Mf_currtime = 0L;
-
- Mf_starttrack();
-
- while ( Mf_toberead > 0 ) {
-
- Mf_currtime += readvarinum(); /* delta time */
- if (midifile_error) return;
-
- c = egetc();
- if (midifile_error) return;
-
- if ( sysexcontinue && c != 0xf7 ) {
- mferror("didn't find expected continuation of a sysex");
- return;
- }
- if ( (c & 0x80) == 0 ) { /* running status? */
- if ( status == 0 ) {
- mferror("unexpected running status");
- return;
- }
- running = 1;
- } else {
- status = c;
- running = 0;
- }
-
- needed = chantype[ (status>>4) & 0xf ];
-
- if ( needed ) { /* ie. is it a channel message? */
-
- if ( running )
- c1 = c;
- else {
- c1 = egetc();
- if (midifile_error) return;
- }
- chanmessage( status, c1, (needed>1) ? egetc() : 0 );
- if (midifile_error) return;
- continue;;
- }
-
- switch ( c ) {
-
- case 0xff: /* meta event */
-
- type = egetc();
- if (midifile_error) return;
- /* watch out - Don't combine the next 2 statements */
- lng = readvarinum();
- if (midifile_error) return;
- lookfor = Mf_toberead - lng;
- msginit();
-
- while ( Mf_toberead > lookfor ) {
- char c = egetc();
- if (midifile_error) return;
- msgadd(c);
- }
- metaevent(type);
- break;
-
- case 0xf0: /* start of system exclusive */
-
- /* watch out - Don't combine the next 2 statements */
- lng = readvarinum();
- if (midifile_error) return;
- lookfor = Mf_toberead - lng;
- msginit();
- msgadd(0xf0);
-
- while ( Mf_toberead > lookfor ) {
- c = egetc();
- if (midifile_error) return;
- msgadd(c);
- }
- if ( c==0xf7 || Mf_nomerge==0 )
- sysex();
- else
- sysexcontinue = 1; /* merge into next msg */
- break;
-
- case 0xf7: /* sysex continuation or arbitrary stuff */
-
- /* watch out - Don't combine the next 2 statements */
- lng = readvarinum();
- if (midifile_error) return;
- lookfor = Mf_toberead - lng;
-
- if ( ! sysexcontinue )
- msginit();
-
- while ( Mf_toberead > lookfor ) {
- c = egetc();
- if (midifile_error) return;
- msgadd(c);
- }
- if ( ! sysexcontinue ) {
- Mf_arbitrary(msgleng(), msg());
- }
- else if ( c == 0xf7 ) {
- sysex();
- sysexcontinue = 0;
- }
- break;
- default:
-
- badbyte(c);
-
- break;
- }
- }
- Mf_endtrack();
- return;
-}
-
-void Midifile_reader::badbyte(int c)
-{
- char buff[32];
-
- (void) sprintf(buff,"unexpected byte: 0x%02x",c);
- mferror(buff);
-}
-
-void Midifile_reader::metaevent(int type)
-{
- int leng = msgleng();
- char *m = msg();
-
- switch ( type ) {
- case 0x00:
- Mf_seqnum(to16bit(m[0],m[1]));
- break;
- case 0x01: /* Text event */
- case 0x02: /* Copyright notice */
- case 0x03: /* Sequence/Track name */
- case 0x04: /* Instrument name */
- case 0x05: /* Lyric */
- case 0x06: /* Marker */
- case 0x07: /* Cue point */
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0c:
- case 0x0d:
- case 0x0e:
- case 0x0f:
- /* These are all text events */
- Mf_text(type,leng,m);
- break;
- case 0x20:
- Mf_chanprefix(m[0]);
- break;
- case 0x21:
- Mf_portprefix(m[0]);
- break;
- case 0x2f: /* End of Track */
- Mf_eot();
- break;
- case 0x51: /* Set tempo */
- Mf_tempo(to32bit(0,m[0],m[1],m[2]));
- break;
- case 0x54:
- Mf_smpte(m[0],m[1],m[2],m[3],m[4]);
- break;
- case 0x58:
- Mf_timesig(m[0],m[1],m[2],m[3]);
- break;
- case 0x59:
- Mf_keysig(m[0],m[1]);
- break;
- case 0x7f:
- Mf_sqspecific(leng,m);
- break;
- default:
- Mf_metamisc(type,leng,m);
- }
-}
-
-
-void Midifile_reader::sysex()
-{
- Mf_sysex(msgleng(), msg());
-}
-
-
-void Midifile_reader::chanmessage(int status, int c1, int c2)
-{
- int chan = status & 0xf;
-
- switch ( status & 0xf0 ) {
- case NOTEOFF:
- Mf_off(chan,c1,c2);
- break;
- case NOTEON:
- Mf_on(chan,c1,c2);
- break;
- case PRESSURE:
- Mf_pressure(chan,c1,c2);
- break;
- case CONTROLLER:
- Mf_controller(chan,c1,c2);
- break;
- case PITCHBEND:
- Mf_pitchbend(chan,c1,c2);
- break;
- case PROGRAM:
- Mf_program(chan,c1);
- break;
- case CHANPRESSURE:
- Mf_chanpressure(chan,c1);
- break;
- }
-}
-
-/* readvarinum - read a varying-length number, and return the */
-/* number of characters it took. */
-
-long Midifile_reader::readvarinum()
-{
- long value;
- int c;
-
- c = egetc();
- if (midifile_error) return 0;
-
- value = (long) c;
- if ( c & 0x80 ) {
- value &= 0x7f;
- do {
- c = egetc();
- if (midifile_error) return 0;
- value = (value << 7) + (c & 0x7f);
- } while (c & 0x80);
- }
- return (value);
-}
-
-long Midifile_reader::to32bit(int c1, int c2, int c3, int c4)
-{
- long value = 0L;
-
- value = (c1 & 0xff);
- value = (value<<8) + (c2 & 0xff);
- value = (value<<8) + (c3 & 0xff);
- value = (value<<8) + (c4 & 0xff);
- return (value);
-}
-
-int Midifile_reader::to16bit(int c1, int c2)
-{
- return ((c1 & 0xff ) << 8) + (c2 & 0xff);
-}
-
-long Midifile_reader::read32bit()
-{
- int c1, c2, c3, c4;
-
- c1 = egetc(); if (midifile_error) return 0;
- c2 = egetc(); if (midifile_error) return 0;
- c3 = egetc(); if (midifile_error) return 0;
- c4 = egetc(); if (midifile_error) return 0;
- return to32bit(c1,c2,c3,c4);
-}
-
-int Midifile_reader::read16bit()
-{
- int c1, c2;
- c1 = egetc(); if (midifile_error) return 0;
- c2 = egetc(); if (midifile_error) return 0;
- return to16bit(c1,c2);
-}
-
-void Midifile_reader::mferror(const char *s)
-{
- Mf_error(s);
- midifile_error = 1;
-}
-
-/* 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(). */
-
-#define MSGINCREMENT 128
-
-Midifile_reader::Midifile_reader()
-{
- Mf_nomerge = 0;
- Mf_currtime = 0L;
- Mf_skipinit = 0;
- Mf_toberead = 0;
-
- Msgbuff = 0; /* message buffer */
- Msgsize = 0; /* Size of currently allocated Msg */
- Msgindex = 0; /* index of next available location in Msg */
-}
-
-void Midifile_reader::finalize()
-{
- if (Msgbuff) Mf_free(Msgbuff, Msgsize);
- Msgbuff = NULL;
-}
-
-
-void Midifile_reader::msginit()
-{
- Msgindex = 0;
-}
-
-char *Midifile_reader::msg()
-{
- return(Msgbuff);
-}
-
-int Midifile_reader::msgleng()
-{
- return(Msgindex);
-}
-
-void Midifile_reader::msgadd(int c)
-{
- /* If necessary, allocate larger message buffer. */
- if ( Msgindex >= Msgsize )
- msgenlarge();
- Msgbuff[Msgindex++] = c;
-}
-
-void Midifile_reader::msgenlarge()
-{
- char *newmess;
- char *oldmess = Msgbuff;
- int oldleng = Msgsize;
-
- Msgsize += MSGINCREMENT;
- newmess = (char *) Mf_malloc((sizeof(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;
- Mf_free(oldmess, oldleng);
- }
- Msgbuff = newmess;
-}
diff --git a/plugins/MidiImport/portsmf/mfmidi.h b/plugins/MidiImport/portsmf/mfmidi.h
deleted file mode 100644
index d0049294bc2..00000000000
--- a/plugins/MidiImport/portsmf/mfmidi.h
+++ /dev/null
@@ -1,98 +0,0 @@
-#define NOTEOFF 0x80
-#define NOTEON 0x90
-#define PRESSURE 0xa0
-#define CONTROLLER 0xb0
-#define PITCHBEND 0xe0
-#define PROGRAM 0xc0
-#define CHANPRESSURE 0xd0
-
-/* These are the strings used in keynote to identify Standard MIDI File */
-/* meta text messages. */
-
-#define METATEXT "Text Event"
-#define METACOPYRIGHT "Copyright Notice"
-#define METASEQUENCE "Sequence/Track Name"
-#define METAINSTRUMENT "Instrument Name"
-#define METALYRIC "Lyric"
-#define METAMARKER "Marker"
-#define METACUE "Cue Point"
-#define METAUNRECOGNIZED "Unrecognized"
-
-
-class Midifile_reader {
-public:
- void midifile();
- int Mf_nomerge; /* 1 => continue'ed system exclusives are */
- /* not collapsed. */
- long Mf_currtime; /* current time in delta-time units */
- int Mf_skipinit; /* 1 if initial garbage should be skipped */
- Midifile_reader();
- // call finalize() when done or you may leak memory.
- void finalize(); /* clean up before deletion */
- // Note: rather than finalize, we should have ~Midifile_reader(),
- // but at least VC++ complains that there is no Mf_free(), even
- // though Mf_free is declared as virtual and this is an abstract
- // class. I don't understand this, so finalize() is a workaround. -RBD
-
-protected:
- int midifile_error;
-
- virtual void *Mf_malloc(size_t size) = 0; /* malloc() */
- virtual void Mf_free(void *obj, size_t size) = 0; /* free() */
- /* Methods to be called while processing the MIDI file. */
- virtual void Mf_starttrack() = 0;
- virtual void Mf_endtrack() = 0;
- virtual int Mf_getc() = 0;
- 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_header(int,int,int) = 0;
- virtual void Mf_on(int,int,int) = 0;
- virtual void Mf_off(int,int,int) = 0;
- virtual void Mf_pressure(int,int,int) = 0;
- virtual void Mf_controller(int,int,int) = 0;
- 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_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;
-
-private:
- long Mf_toberead;
-
- long readvarinum();
- long read32bit();
- int read16bit();
- void msgenlarge();
- char *msg();
- int readheader();
- void readtrack();
- void sysex();
- void msginit();
- int egetc();
- int msgleng();
-
- int readmt(const char *, int);
- long to32bit(int,int,int,int);
- int to16bit(int,int);
- void mferror(const char *);
- void badbyte(int);
- void metaevent(int);
- void msgadd(int);
- void chanmessage(int,int,int);
-
- char *Msgbuff;
- long Msgsize;
- long Msgindex;
-};
-
-
diff --git a/plugins/MidiImport/portsmf/strparse.cpp b/plugins/MidiImport/portsmf/strparse.cpp
deleted file mode 100644
index 7665b4ae058..00000000000
--- a/plugins/MidiImport/portsmf/strparse.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-#include
-#include
-// #include -- for debugging (cout)
-#include "ctype.h"
-using namespace std;
-#include "strparse.h"
-
-void String_parse::skip_space()
-{
- while ((*str)[pos] && isspace((*str)[pos])) {
- pos = pos + 1;
- }
-}
-
-
-char String_parse::peek()
-{
- return (*str)[pos];
-}
-
-
-void String_parse::get_nonspace_quoted(string &field)
-{
- field.clear();
- skip_space();
- bool quoted = false;
- if ((*str)[pos] == '"') {
- quoted = true;
- field.append(1, '"');
- pos = pos + 1;
- }
- while ((*str)[pos] && (quoted || !isspace((*str)[pos]))) {
- if ((*str)[pos] == '"') {
- if (quoted) {
- field.append(1, '"');
- pos = pos + 1;
- }
- return;
- }
- if ((*str)[pos] == '\\') {
- pos = pos + 1;
- }
- if ((*str)[pos]) {
- field.append(1, (*str)[pos]);
- pos = pos + 1;
- }
- }
-}
-
-
-char *escape_chars[] = { (char *) "\\n", (char *)"\\t", (char *)"\\\\", (char *)"\\r", (char *) "\\\""};
-
-
-void string_escape(string &result, char *str, const char *quote)
-{
- int length = (int) strlen(str);
- if (quote[0]) {
- result.append(1, quote[0]);
- }
- 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]);
- if (special) {
- result.append(escape_chars[special - chars]);
- } else {
- result.append(1, str[i]);
- }
- } else {
- result.append(1, str[i]);
- }
- }
- result.append(1, quote[0]);
-}
-
-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,
- 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
deleted file mode 100644
index 74f01591974..00000000000
--- a/plugins/MidiImport/portsmf/strparse.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// strparse.h -- header for String_parse class
-
-class String_parse {
-public:
- int pos;
- std::string *str;
- void init(std::string *s) {
- str = s;
- pos = 0;
- }
- void skip_space();
- char peek();
- void get_nonspace_quoted(std::string &field);
- // get the remaining characters, skipping initial spaces and final return
- void get_remainder(std::string &field);
-};
-
-void string_escape(std::string &result, char *s, const char *quote);
diff --git a/plugins/MidiImport/portsmf/trace.cpp b/plugins/MidiImport/portsmf/trace.cpp
deleted file mode 100644
index 7c1999db570..00000000000
--- a/plugins/MidiImport/portsmf/trace.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// trace.cpp -- debugging print function
-//
-// (I think this was created to provide a generic print function
-// for use in non-command-line Windows applications where printf
-// does not work. Currently, it is not used, but kept around for
-// possible debugging needs. -RBD)
-
-#include "stdarg.h"
-#include "stdio.h"
-#include "crtdbg.h"
-
-
-void trace(char *format, ...)
-{
- char msg[256];
- va_list args;
- va_start(args, format);
- _vsnprintf(msg, 256, format, args);
- va_end(args);
-#ifdef _DEBUG
- _CrtDbgReport(_CRT_WARN, NULL, NULL, NULL, msg);
-#else
- printf(msg);
-#endif
-}
diff --git a/plugins/MidiImport/portsmf/trace.h b/plugins/MidiImport/portsmf/trace.h
deleted file mode 100644
index 5726f0c5de6..00000000000
--- a/plugins/MidiImport/portsmf/trace.h
+++ /dev/null
@@ -1,2 +0,0 @@
-void trace(char *format, ...);
-
diff --git a/plugins/SmfImport/CMakeLists.txt b/plugins/SmfImport/CMakeLists.txt
new file mode 100644
index 00000000000..d5a15c1f79c
--- /dev/null
+++ b/plugins/SmfImport/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Importer for SMF-like file format, such as .mid, .ove, .wrk
+
+# check for drumstick-file
+IF(WANT_QT5)
+ PKG_CHECK_MODULES(DRUMSTICK_FILE REQUIRED drumstick-file>=1.0.0)
+ELSE()
+ PKG_CHECK_MODULES(DRUMSTICK_FILE REQUIRED drumstick-file>=0.5.0)
+ENDIF()
+
+IF(NOT DRUMSTICK_FILE_FOUND)
+ IF(WANT_QT5)
+ MESSAGE(FATAL_ERROR "LMMS requires libdrumstick-file and libdrumstick-dev >= 1.0.0 with Qt5 - please install, remove CMakeCache.txt and try again!")
+ ELSE()
+ MESSAGE(FATAL_ERROR "LMMS requires libdrumstick-file and libdrumstick-dev >= 0.5.0 with Qt4 - please install, remove CMakeCache.txt and try again!")
+ ENDIF()
+ENDIF(NOT DRUMSTICK_FILE_FOUND)
+
+INCLUDE(BuildPlugin)
+
+LINK_DIRECTORIES(${DRUMSTICK_FILE_LIBDIR})
+INCLUDE_DIRECTORIES(${DRUMSTICK_FILE_INCLUDEDIR})
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
+
+BUILD_PLUGIN(smfimport SmfImport.cpp commonReader.cpp midiReader.cpp oveReader.cpp wrkReader.cpp
+ MOCFILES SmfImport.h commonReader.h midiReader.h oveReader.h wrkReader.h )
+
+IF(NOT LMMS_BUILD_LINUX)
+ IF(WANT_QT5)
+ TARGET_LINK_LIBRARIES(smfimport ${DRUMSTICK_FILE_LIBRARIES} Qt5::Core)
+ ELSE()
+ TARGET_LINK_LIBRARIES(smfimport ${DRUMSTICK_FILE_LIBRARIES} -lQtCore4)
+ ENDIF()
+ELSE()
+ TARGET_LINK_LIBRARIES(smfimport ${DRUMSTICK_FILE_LIBRARIES})
+ENDIF()
diff --git a/plugins/SmfImport/SmfImport.cpp b/plugins/SmfImport/SmfImport.cpp
new file mode 100644
index 00000000000..9b83e9008de
--- /dev/null
+++ b/plugins/SmfImport/SmfImport.cpp
@@ -0,0 +1,234 @@
+/*
+ * SmfImport.h - support for importing SMF-liked files
+ *
+ * Copyright (c) 2016-2017 Tony Chyi
+ *
+ * This file is part of LMMS - http://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.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "TrackContainer.h"
+#include "InstrumentTrack.h"
+#include "AutomationTrack.h"
+#include "AutomationPattern.h"
+#include "ConfigManager.h"
+#include "Pattern.h"
+#include "Instrument.h"
+#include "GuiApplication.h"
+#include "MainWindow.h"
+#include "MidiTime.h"
+#include "debug.h"
+#include "embed.h"
+#include "Song.h"
+
+#include "SmfImport.h"
+#include "midiReader.h"
+#include "oveReader.h"
+#include "wrkReader.h"
+
+extern "C"
+{
+
+ Plugin::Descriptor PLUGIN_EXPORT smfimport_plugin_descriptor =
+ {
+ STRINGIFY( PLUGIN_NAME ),
+ "SMF Import",
+ QT_TRANSLATE_NOOP( "pluginBrowser",
+ "Filter for importing MIDI-like files into LMMS" ),
+ "Tony Chyi ",
+ 0x0100,
+ Plugin::ImportFilter,
+ NULL,
+ NULL,
+ NULL
+ } ;
+
+}
+
+
+
+SmfImport::SmfImport( const QString &_file ):
+ ImportFilter( _file, &smfimport_plugin_descriptor )
+{
+
+}
+
+SmfImport::~SmfImport()
+{
+ closeFile();
+}
+
+bool SmfImport::tryImport( TrackContainer *tc )
+{
+
+#ifdef LMMS_HAVE_FLUIDSYNTH
+
+ if( gui != NULL && ConfigManager::inst()->defaultSoundfont().isEmpty() )
+ {
+ QMessageBox::information( gui->mainWindow(),
+ tr( "Setup incomplete" ),
+ tr( "You do not have set up a default soundfont in "
+ "the settings dialog (Edit->Settings). "
+ "Therefore no sound will be played back after "
+ "importing this MIDI file. You should download "
+ "a General MIDI soundfont, specify it in "
+ "settings dialog and try again." ) );
+ }
+
+#else
+
+ if( gui )
+ {
+ QMessageBox::information( gui->mainWindow(),
+ tr( "Setup incomplete" ),
+ tr( "You did not compile LMMS with support for "
+ "SoundFont2 player, which is used to add default "
+ "sound to imported MIDI files. "
+ "Therefore no sound will be played back after "
+ "importing this MIDI file." ) );
+ }
+
+ return false;
+#endif
+
+ if( !openFile() )
+ {
+ return false;
+ }
+
+ filename = file().fileName();
+
+ switch( readID() )
+ {
+ case makeID( 'M', 'T', 'h', 'd' ):
+ printf( "SmfImport::tryImport(): found MThd\n" );
+ return readSMF( tc );
+
+ case makeID( 'R', 'I', 'F', 'F' ):
+ printf( "SmfImport::tryImport(): found RIFF\n" );
+ return readRIFF( tc );
+
+ case makeID( 'O', 'V', 'S', 'C' ):
+ printf( "SmfImport::tryImport(): found OVSC\n" );
+ return readOve( tc );
+
+ case makeID( 'C', 'A', 'K', 'E' ):
+ printf( "SmfImport::tryImport(): found CAKEWALK\n" );
+ return readWrk( tc );
+
+ default:
+ printf( "MidiImport::tryImport(): not a Standard MIDI "
+ "file\n" );
+ return false;
+ }
+
+}
+
+bool SmfImport::readWrk( TrackContainer *tc )
+{
+ wrkReader mr( tc );
+ mr.read( filename );
+ return true;
+}
+
+bool SmfImport::readOve( TrackContainer *tc )
+{
+ oveReader mr( tc );
+ mr.read( filename );
+ return true;
+}
+
+bool SmfImport::readSMF( TrackContainer *tc )
+{
+ midiReader mr( tc );
+ mr.read( filename );
+ return true;
+}
+
+bool SmfImport::readRIFF( TrackContainer* tc )
+{
+ // skip file length
+ skip( 4 );
+
+ // check file type ("RMID" = RIFF MIDI)
+ if( readID() != makeID( 'R', 'M', 'I', 'D' ) )
+ {
+invalid_format:
+ qWarning( "MidiImport::readRIFF(): invalid file format" );
+ return false;
+ }
+
+ // search for "data" chunk
+ while( 1 )
+ {
+ const int id = readID();
+ const int len = read32LE();
+
+ if( file().atEnd() )
+ {
+data_not_found:
+ qWarning( "MidiImport::readRIFF(): data chunk not found" );
+ return false;
+ }
+
+ if( id == makeID( 'd', 'a', 't', 'a' ) )
+ {
+ break;
+ }
+
+ if( len < 0 )
+ {
+ goto data_not_found;
+ }
+
+ skip( ( len + 1 ) & ~1 );
+ }
+
+ // the "data" chunk must contain data in SMF format
+ if( readID() != makeID( 'M', 'T', 'h', 'd' ) )
+ {
+ goto invalid_format;
+ }
+
+ return readSMF( tc );
+}
+
+void SmfImport::error()
+{
+
+}
+
+
+extern "C"
+{
+
+// necessary for getting instance out of shared lib
+ Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
+ {
+ return new SmfImport( QString::fromUtf8(
+ static_cast( _data ) ) );
+ }
+
+
+}
diff --git a/plugins/MidiImport/MidiImport.h b/plugins/SmfImport/SmfImport.h
similarity index 76%
rename from plugins/MidiImport/MidiImport.h
rename to plugins/SmfImport/SmfImport.h
index d4c7a0f4a58..9e0145f5abf 100644
--- a/plugins/MidiImport/MidiImport.h
+++ b/plugins/SmfImport/SmfImport.h
@@ -1,7 +1,7 @@
/*
- * MidiImport.h - support for importing MIDI-files
+ * SmfImport.cpp - support for importing SMF-liked files
*
- * Copyright (c) 2005-2014 Tobias Doerffel
+ * Copyright (c) 2016-2017 Tony Chyi
*
* This file is part of LMMS - http://lmms.io
*
@@ -22,52 +22,56 @@
*
*/
-#ifndef _MIDI_IMPORT_H
-#define _MIDI_IMPORT_H
+#ifndef SMF_IMPORT_H
+#define SMF_IMPORT_H
#include
#include
#include
+#include
#include "MidiEvent.h"
#include "ImportFilter.h"
-
-class MidiImport : public ImportFilter
+class SmfImport : public ImportFilter
{
Q_OBJECT
public:
- MidiImport( const QString & _file );
- virtual ~MidiImport();
+ SmfImport( const QString & _file );
+ virtual ~SmfImport();
virtual PluginView * instantiateView( QWidget * )
{
- return( NULL );
+ return NULL;
}
-
private:
- virtual bool tryImport( TrackContainer* tc );
+ virtual bool tryImport( TrackContainer *tc );
bool readSMF( TrackContainer* tc );
bool readRIFF( TrackContainer* tc );
- bool readTrack( int _track_end, QString & _track_name );
-
- void error( void );
+ bool readOve( TrackContainer* tc );
+ bool readWrk( TrackContainer* tc );
+ void error();
inline int readInt( int _bytes )
{
int c, value = 0;
+
do
{
c = readByte();
+
if( c == -1 )
{
return( -1 );
}
+
value = ( value << 8 ) | c;
- } while( --_bytes );
+ }
+ while( --_bytes );
+
return( value );
}
inline int read32LE()
@@ -82,26 +86,31 @@ class MidiImport : public ImportFilter
{
int c = readByte();
int value = c & 0x7f;
+
if( c & 0x80 )
{
c = readByte();
value = ( value << 7 ) | ( c & 0x7f );
+
if( c & 0x80 )
{
c = readByte();
value = ( value << 7 ) | ( c & 0x7f );
+
if( c & 0x80 )
{
c = readByte();
value = ( value << 7 ) | c;
+
if( c & 0x80 )
{
return -1;
}
}
}
- }
- return( !file().atEnd() ? value : -1 );
+ }
+
+ return( !file().atEnd() ? value : -1 );
}
inline int readID()
@@ -117,12 +126,7 @@ class MidiImport : public ImportFilter
}
}
-
- typedef QVector > EventVector;
- EventVector m_events;
- int m_timingDivision;
-
-} ;
-
+ QString filename;
+};
#endif
diff --git a/plugins/SmfImport/commonReader.cpp b/plugins/SmfImport/commonReader.cpp
new file mode 100644
index 00000000000..63646c1c8f8
--- /dev/null
+++ b/plugins/SmfImport/commonReader.cpp
@@ -0,0 +1,405 @@
+/*
+ * commonReader.cpp - import backend.
+ *
+ * Copyright (c) 2016-2017 Tony Chyi
+ *
+ * This file is part of LMMS - http://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.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "GuiApplication.h"
+#include "TrackContainer.h"
+#include "Engine.h"
+#include "Song.h"
+#include "AutomationPattern.h"
+#include "MidiTime.h"
+#include "MainWindow.h"
+
+#include "SmfMidiCC.h"
+#include "SmfMidiChannel.h"
+
+#include "commonReader.h"
+
+#define CHECK_TRACK track = ( track == noTrack ? chan : track );
+#define PITCH_RANGE_RPN_CODE {track, 0}
+#define CC_RPN_SEND rpn_data[0] = track; \
+ rpn_data[1] = value;
+
+#define NOTE_EVENT_DEFINITION const int time = 0; \
+ const int channel = time + 1; \
+ const int note_pitch = channel + 1; \
+ const int note_vol = note_pitch + 1;
+
+commonReader::commonReader( TrackContainer *tc, const QString hintText ):
+ m_tc( tc ),
+ beatsPerTact( 4 ),
+ pitchBendMultiply( defaultPitchRange ),
+ pd( hintText, TrackContainer::tr( "Cancel" ),
+ 0, preTrackSteps, gui->mainWindow() )
+{
+ m_currentTrackName.first = -1;
+ pd.setWindowTitle( TrackContainer::tr( "Please wait..." ) );
+ pd.setWindowModality( Qt::WindowModal );
+ pd.setMinimumDuration( 0 );
+ pd.setValue( 0 );
+
+
+ MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel();
+ timeSigNumeratorPat = AutomationPattern::globalAutomationPattern(
+ &timeSigMM.numeratorModel() );
+ timeSigDenominatorPat = AutomationPattern::globalAutomationPattern(
+ &timeSigMM.denominatorModel() );
+
+ ticksPerBeat = DefaultTicksPerTact / beatsPerTact;
+
+ if( note_list.size() )
+ note_list.clear();
+
+ if( rpn_msbs.size() )
+ rpn_msbs.clear();
+
+ if( rpn_lsbs.size() )
+ rpn_lsbs.clear();
+
+ AutomationPattern * tap = m_tc->tempoAutomationPattern();
+ tap->clear();
+}
+
+commonReader::~commonReader()
+{
+ printf( "destory fileReader\n" );
+
+ for( int c = 0; c < 256; c++ )
+ {
+ if( !chs[c].hasNotes && chs[c].it )
+ {
+ printf( " Should remove empty track\n" );
+ // must delete trackView first - but where is it?
+ //m_tc->removeTrack( chs[c].it );
+ //chs[c].it->deleteLater();
+ }
+ }
+
+}
+
+void commonReader::CCHandler( long tick, int track, int ctl, int value )
+{
+ QString trackName;
+
+ if( m_currentTrackName.first == m_currentTrack )
+ {
+ trackName = m_currentTrackName.second;
+ }
+ else
+ {
+ trackName = TrackContainer::tr( "Track %1" ).arg( track + 1 );
+ }
+
+ SmfMidiChannel * ch = chs[track].create( m_tc, trackName );
+ AutomatableModel * objModel = NULL;
+ int * rpn_data = new int[2];
+
+ bool flag_rpn_msb = false;
+ bool flag_rpn_lsb = false;
+
+ if( ctl <= 129 )
+ {
+ switch( ctl )
+ {
+ case bankEventId:
+ if( ch->isSF2 && ch->it_inst )
+ {
+ if( track != 9 )
+ {
+ objModel = ch->it_inst->childModel( "bank" );
+ printf( "Tick=%ld: BANK SELECT %d\n", tick, value );
+ }
+ }
+
+ break;
+
+ case 6:
+ for( int c = 0; c < rpn_msbs.size(); c++ )
+ {
+ rpn_data = rpn_msbs[c];
+
+ if( rpn_data[0] == track && rpn_data[1] == 0 )
+ {
+ flag_rpn_msb = true;
+ rpn_msbs.removeAt( c );
+ delete rpn_data;
+ }
+ }
+
+ for( int c = 0; c < rpn_lsbs.size(); c++ )
+ {
+ rpn_data = rpn_lsbs[c];
+
+ if( rpn_data[0] == track && rpn_data[1] == 0 )
+ {
+ flag_rpn_lsb = true;
+ rpn_lsbs.removeAt( c );
+ delete rpn_data;
+ }
+ }
+
+ if( flag_rpn_lsb && flag_rpn_msb )
+ {
+ objModel = ch->it->pitchRangeModel();
+ pitchBendMultiply = value;
+ }
+
+ break;
+
+ case volumeEventId:
+ objModel = ch->it->volumeModel();
+ value = value * 100 / 127;
+ break;
+
+ case panEventId:
+ objModel = ch->it->panningModel();
+ // value may be nagetive.
+ value = value * 100 / 127;
+ break;
+
+ // RPN LSB
+ case 100:
+ CC_RPN_SEND
+ rpn_lsbs << rpn_data;
+ break;
+
+ // RPN MSB
+ case 101:
+ CC_RPN_SEND
+ rpn_msbs << rpn_data;
+ break;
+
+ case pitchBendEventId:
+ objModel = ch->it->pitchModel();
+ value = value * 100 / 8192 * pitchBendMultiply;
+ break;
+
+ case programEventId:
+ if( ch->isSF2 && ch->it_inst )
+ objModel = ch->it_inst->childModel( "patch" );
+
+ break;
+
+ default:
+ // TODO: something useful for other CCs
+ printf( "Tick=%ld: Unused CC %d with value=%d\n",
+ tick, ctl, value );
+ break;
+ }
+
+ if( objModel )
+ {
+ if( tick == 0 )
+ objModel->setInitValue( value );
+ else
+ {
+ if( ccs[track][ctl].at == NULL )
+ {
+ ccs[track][ctl].create( m_tc, trackName + " > " + objModel->displayName() );
+ }
+
+ ccs[track][ctl].putValue( tick * tickRate , objModel, value );
+ }
+ }
+ }
+
+}
+
+void commonReader::programHandler( long tick, int chan, int patch,
+ int track )
+{
+ CHECK_TRACK
+ QString trackName = TrackContainer::tr( "Track %1" ).arg( chan + 1 );
+ SmfMidiChannel * ch = chs[track].create( m_tc, trackName );
+
+ if( ch->isSF2 )
+ {
+ // AFAIK, 128 should be the standard bank for drums in SF2.
+ // If not, this has to be made configurable.
+ ch->it_inst->childModel( "bank" )->setValue( chan != 9 ? 0 : 128 );
+
+ if( tick == 0 )
+ ch->it_inst->childModel( "patch" )->setValue( patch );
+ else
+ CCHandler( tick, track, programEventId, patch );
+ }
+ else
+ {
+ const QString num = QString::number( patch );
+ const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat";
+ const QString dir = "/usr/share/midi/"
+ "freepats/Tone_000/";
+ const QStringList files = QDir( dir ).
+ entryList( QStringList( filter ) );
+
+ if( ch->it_inst && !files.empty() )
+ {
+ ch->it_inst->loadFile( dir + files.front() );
+ }
+ }
+
+}
+
+void commonReader::timeSigHandler( long tick, int num, int den )
+{
+ printf( "Another timesig at %f\n", tick * tickRate );
+ timeSigNumeratorPat->putValue( tick * tickRate, num );
+ timeSigDenominatorPat->putValue( tick * tickRate, den );
+ pd.setValue( preTrackSteps );
+}
+
+void commonReader::tempoHandler( long tick, int tempo )
+{
+ AutomationPattern * tap = m_tc->tempoAutomationPattern();
+
+ if( tap )
+ {
+ tap->putValue( static_cast( tick ) * tickRate, tempo );
+ }
+}
+
+void commonReader::textHandler( int text_type, const QString &data , int track )
+{
+ switch ( text_type )
+ {
+ case 3:
+
+ // Track Name
+ m_currentTrack = ( track == -10 ? m_currentTrack : track );
+
+ if ( m_currentTrack == noTrack ) // Meta name, i.e. The real title/name of the current song file
+ {
+ return; // Don't know how to handle this in LMMS
+ }
+
+ m_currentTrackName.first = m_currentTrack;
+ m_currentTrackName.second = data;
+
+ if( chs[m_currentTrack].it )
+ {
+ chs[m_currentTrack].setName( data );
+ }
+ else
+ {
+ chs[m_currentTrack].create( m_tc, data );
+ }
+
+ case 5:
+
+ // Lyrics
+ // How to handle lyrics?
+ case 6:
+
+ // MIDI Marker Description
+ // How to handle markers & cue points?
+
+ default:
+ printf( "Unknown Text Event: %d %s\n", text_type, data.toStdString().c_str() );
+ }
+
+}
+
+void commonReader::timeBaseHandler( int timebase )
+{
+ tickRate = ticksPerBeat / static_cast( timebase );
+}
+
+void commonReader::trackStartHandler()
+{
+ m_currentTrack ++;
+}
+
+// when use this, addNoteEvent() should be called later, otherwise it will cause memeory leak.
+void commonReader::insertNoteEvent( long tick, int chan, int pitch,
+ int vol, int track )
+{
+ NOTE_EVENT_DEFINITION
+ CHECK_TRACK
+
+ int *note = new int[4];
+ note[time] = tick;
+ note[channel] = track;
+ note[note_pitch] = pitch;
+ note[note_vol] = vol;
+ note_list << note;
+}
+
+void commonReader::addNoteEvent(long tick, int chan,
+ int pitch, int vol, int track )
+{
+ NOTE_EVENT_DEFINITION
+ CHECK_TRACK
+
+ if( !note_list.size() && vol )
+ {
+ addNoteEvent( tick, chan, pitch, vol, 1, track );
+ }
+
+ for( int c = 0; c < note_list.size(); c++ )
+ {
+ int *note;
+ note = note_list[c];
+
+ if( note[channel] == track && note[note_pitch] == pitch
+ && tick >= note[time] )
+ {
+ int dur = tick - note[time];
+ addNoteEvent( note[time], chan, pitch, note[note_vol], dur, track );
+ note_list.removeAt( c );
+ delete note;
+ break;
+ }
+ }
+
+ if( pd.maximum() <= chan + preTrackSteps )
+ pd.setMaximum( pd.maximum() + preTrackSteps );
+
+ if( pd.value() <= chan + preTrackSteps )
+ pd.setValue( chan + preTrackSteps );
+
+}
+
+void commonReader::addNoteEvent( long tick, int chan, int pitch, int vol,
+ int dur, int track )
+{
+ CHECK_TRACK
+
+ QString trackName = TrackContainer::tr( "Track %1" ).arg( track + 1 );
+ SmfMidiChannel * ch = chs[track].create( m_tc, trackName );
+ double realDur = dur * tickRate;
+ Note n( ( realDur < 1 ? 1 : realDur ), static_cast( tick ) * tickRate, pitch - 12, vol * 200 / 127 );
+ ch->addNote( n );
+
+}
+
+void commonReader::errorHandler( const QString &errorStr )
+{
+ printf( "SmfImport::readFile(): got error %s\n",
+ errorStr.toStdString().c_str() );
+}
diff --git a/plugins/SmfImport/commonReader.h b/plugins/SmfImport/commonReader.h
new file mode 100644
index 00000000000..ba0fd39c445
--- /dev/null
+++ b/plugins/SmfImport/commonReader.h
@@ -0,0 +1,112 @@
+/*
+ * commonReader.h - import backend.
+ *
+ * Copyright (c) 2016-2017 Tony Chyi
+ *
+ * This file is part of LMMS - http://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 COMMON_READER_H
+#define COMMON_READER_H
+
+#include
+#include
+#include
+
+#include "TrackContainer.h"
+#include "MeterModel.h"
+#include "AutomationPattern.h"
+
+#include "SmfMidiCC.h"
+#include "SmfMidiChannel.h"
+
+#define makeID(_c0, _c1, _c2, _c3) \
+ ( 0 | \
+ ( ( _c0 ) | ( ( _c1 ) << 8 ) | ( ( _c2 ) << 16 ) | ( ( _c3 ) << 24 ) ) )
+
+const int defaultPitchRange = 2;
+const int preTrackSteps = 2;
+
+const int bankEventId = 0;
+const int volumeEventId = 7;
+const int panEventId = 10;
+const int pitchBendEventId = 128;
+const int programEventId = 129;
+
+const int noTrack = -1;
+
+class commonReader : public QObject
+{
+ Q_OBJECT
+public:
+ commonReader( TrackContainer *tc, const QString hintText );
+ ~commonReader();
+protected:
+ virtual void errorHandler( const QString &errorStr );
+ void CCHandler( long tick, int track, int ctl, int value );
+ void programHandler( long tick, int chan, int patch, int track = noTrack );
+ void timeSigHandler( long tick, int num, int den );
+ void tempoHandler( long tick, int tempo );
+ void textHandler( int text_type, const QString& data , int track = -10 );
+ void timeBaseHandler( int timebase );
+ void trackStartHandler();
+
+ void insertNoteEvent( long tick, int chan, int pitch, int vol, int track = noTrack );
+
+ void addNoteEvent( long tick, int chan, int pitch, int vol, int track = noTrack );
+ void addNoteEvent( long tick, int chan, int pitch, int vol, int dur, int track );
+
+
+ TrackContainer *m_tc;
+
+ // 128 CC + Pitch Bend + Program
+ SmfMidiCC ccs[256][130];
+ SmfMidiChannel chs[256];
+
+ AutomationPattern * timeSigNumeratorPat;
+ AutomationPattern * timeSigDenominatorPat;
+
+ double beatsPerTact;
+ double ticksPerBeat;
+ double tickRate; // convert ove tick to lmms tick.
+
+ int pitchBendMultiply;
+ int m_currentTrack = -2;
+ QPair m_currentTrackName;
+
+ QProgressDialog pd;
+
+ /*
+ * record note event.
+ * tick, channel, pitch, vol.
+ */
+ QList note_list;
+
+ /*
+ * record rpn event.
+ * chan, value
+ */
+ QList rpn_msbs;
+ QList rpn_lsbs;
+
+
+};
+
+#endif
+
diff --git a/plugins/SmfImport/midiReader.cpp b/plugins/SmfImport/midiReader.cpp
new file mode 100644
index 00000000000..cdf10556596
--- /dev/null
+++ b/plugins/SmfImport/midiReader.cpp
@@ -0,0 +1,166 @@
+/*
+ * midiReader.cpp - support for importing MIDI files
+ *
+ * Copyright (c) 2005-2014 Tobias Doerffel
+ *
+ * This file is part of LMMS - http://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.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "GuiApplication.h"
+#include "TrackContainer.h"
+#include "Engine.h"
+#include "Song.h"
+#include "AutomationPattern.h"
+#include "MidiTime.h"
+#include "MainWindow.h"
+
+#include "SmfMidiCC.h"
+#include "SmfMidiChannel.h"
+
+#include "midiReader.h"
+
+midiReader::midiReader( TrackContainer* tc ) :
+ commonReader( tc, commonReader::tr( "Importing %1 file..." ).arg( "MIDI" ) ),
+ m_seq( new drumstick::QSmf )
+{
+ // Connect to slots.
+ connect( m_seq, SIGNAL( signalSMFTimeSig( int, int, int, int ) ),
+ this, SLOT( timeSigEvent( int, int, int, int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFTempo( int ) ),
+ this, SLOT( tempoEvent( int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFText( int, QString ) ),
+ this, SLOT( textEvent( int, QString ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFError( QString ) ),
+ this, SLOT( errorHandler( QString ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFCtlChange( int, int, int ) ),
+ this, SLOT( ctlChangeEvent( int, int, int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFPitchBend( int, int ) ),
+ this, SLOT( pitchBendEvent( int, int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFNoteOn( int, int, int ) ),
+ this, SLOT( noteOnEvent( int, int, int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFNoteOff( int, int, int ) ),
+ this, SLOT( noteOffEvent( int, int, int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFProgram( int, int ) ),
+ this, SLOT( programEvent( int, int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFHeader( int, int, int ) ),
+ this, SLOT( headerEvent( int, int, int ) ) );
+
+ connect( m_seq, SIGNAL( signalSMFTrackStart() ), this,
+ SLOT( startTrackEvent() ) );
+
+}
+
+midiReader::~midiReader()
+{
+ printf( "destroy midiReader\n" );
+ delete m_seq;
+}
+
+void midiReader::read( QString &fileName )
+{
+ m_seq->readFromFile( fileName );
+}
+
+// Slots below.
+
+// b0: Numerator
+// b1: Denominator (exponent in a power of two)
+// b2: Number of MIDI clocks per metronome click
+// b3: Number of notated 32nd notes per 24 MIDI clocks
+void midiReader::timeSigEvent( int b0, int b1, int b2, int b3 )
+{
+ timeSigHandler( m_seq->getCurrentTime(), b0, 1 << b1 );
+}
+
+void midiReader::tempoEvent( int tempo )
+{
+ tempoHandler( m_seq->getCurrentTime(), 60000000 / tempo );
+}
+
+void midiReader::textEvent( int text_type, const QString &data )
+{
+ if ( data.length() )
+ {
+ textHandler( text_type, data );
+ }
+}
+
+void midiReader::startTrackEvent()
+{
+ trackStartHandler();
+}
+
+void midiReader::errorHandler( const QString &errorStr )
+{
+ printf( "MidiImport::readSMF(): got error %s at %ld\n",
+ errorStr.toStdString().c_str(), m_seq->getCurrentTime() );
+}
+
+void midiReader::ctlChangeEvent( int chan, int ctl, int value )
+{
+ CCHandler( m_seq->getCurrentTime(), chan, ctl, value );
+}
+
+void midiReader::pitchBendEvent( int chan, int value )
+{
+ CCHandler( m_seq->getCurrentTime(), chan, pitchBendEventId, value );
+}
+
+void midiReader::noteOnEvent( int chan, int pitch, int vol )
+{
+ if( vol )
+ {
+ insertNoteEvent( m_seq->getCurrentTime(), chan, pitch, vol );
+ }
+ else
+ {
+ addNoteEvent( m_seq->getCurrentTime(), chan, pitch, vol );
+ }
+}
+
+void midiReader::noteOffEvent( int chan, int pitch, int vol )
+{
+ addNoteEvent( m_seq->getCurrentTime(), chan, pitch, vol );
+}
+
+void midiReader::headerEvent( int format, int ntrks, int division )
+{
+ timeBaseHandler( division );
+ pd.setMaximum( ntrks + preTrackSteps );
+}
+
+void midiReader::programEvent( int chan, int patch )
+{
+ programHandler( m_seq->getCurrentTime(), chan, patch );
+}
diff --git a/plugins/SmfImport/midiReader.h b/plugins/SmfImport/midiReader.h
new file mode 100644
index 00000000000..f153f31ab21
--- /dev/null
+++ b/plugins/SmfImport/midiReader.h
@@ -0,0 +1,54 @@
+/*
+ * midiReader.h - support for importing MIDI files
+ *
+ * Copyright (c) 2005-2014 Tobias Doerffel
+ *
+ * This file is part of LMMS - http://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 MIDI_READER_H
+#define MIDI_READER_H
+
+#include
+
+#include "commonReader.h"
+class midiReader : public commonReader
+{
+ Q_OBJECT
+public:
+ midiReader( TrackContainer *tc );
+ ~midiReader();
+ void read( QString &fileName );
+public slots:
+ void headerEvent( int format, int ntrks, int division );
+ void noteOnEvent( int chan, int pitch, int vol );
+ void noteOffEvent( int chan, int pitch, int vol );
+ void ctlChangeEvent( int chan, int ctl, int value );
+ void pitchBendEvent( int chan, int value );
+ void programEvent( int chan, int patch );
+ void timeSigEvent( int b0, int b1, int b2, int b3 );
+ void tempoEvent( int tempo );
+ void textEvent( int text_type, const QString& data );
+ void startTrackEvent();
+ void errorHandler( const QString& errorStr );
+private:
+ drumstick::QSmf *m_seq;
+};
+
+#endif
diff --git a/plugins/SmfImport/oveReader.cpp b/plugins/SmfImport/oveReader.cpp
new file mode 100644
index 00000000000..8f5186684a4
--- /dev/null
+++ b/plugins/SmfImport/oveReader.cpp
@@ -0,0 +1,176 @@
+/*
+ * oveReader.cpp - support for importing Overture files
+ *
+ * Copyright (c) 2016-2017 Tony Chyi
+ *
+ * This file is part of LMMS - http://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.
+ *
+ */
+
+#include
+#include
+#include
+#include